1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol, Angelo Salese
3 /***************************************************************************
4 
5     Hudson/NEC HuC6272 "King" device
6 
7     TODO:
8     - Use NSCSI instead of legacy one!
9     - ADPCM Transfer is correct?
10 
11     ADPCM related patents:
12     - https://patents.google.com/patent/US5692099
13     - https://patents.google.com/patent/US6453286
14     - https://patents.google.com/patent/US5548655A
15 
16 ***************************************************************************/
17 
18 #include "emu.h"
19 
20 #include "video/huc6272.h"
21 
22 
23 
24 //**************************************************************************
25 //  GLOBAL VARIABLES
26 //**************************************************************************
27 
28 // device type definition
29 DEFINE_DEVICE_TYPE(HUC6272, huc6272_device, "huc6272", "Hudson HuC6272 \"King\"")
30 
microprg_map(address_map & map)31 void huc6272_device::microprg_map(address_map &map)
32 {
33 	if (!has_configured_map(0))
34 		map(0x00, 0x0f).ram().share("microprg_ram");
35 }
36 
kram_map(address_map & map)37 void huc6272_device::kram_map(address_map &map)
38 {
39 	if (!has_configured_map(1))
40 	{
41 		map(0x000000, 0x0fffff).ram().share("kram_page0");
42 		map(0x100000, 0x1fffff).ram().share("kram_page1");
43 	}
44 }
45 
46 
47 //**************************************************************************
48 //  LIVE DEVICE
49 //**************************************************************************
50 
51 //-------------------------------------------------
52 //  huc6272_device - constructor
53 //-------------------------------------------------
54 
huc6272_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)55 huc6272_device::huc6272_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
56 	: device_t(mconfig, HUC6272, tag, owner, clock),
57 		device_memory_interface(mconfig, *this),
58 		m_huc6271(*this, finder_base::DUMMY_TAG),
59 		m_cdda_l(*this, "cdda_l"),
60 		m_cdda_r(*this, "cdda_r"),
61 		m_program_space_config("microprg", ENDIANNESS_LITTLE, 16, 4, 0, address_map_constructor(FUNC(huc6272_device::microprg_map), this)),
62 		m_data_space_config("kram", ENDIANNESS_LITTLE, 32, 21, 0, address_map_constructor(FUNC(huc6272_device::kram_map), this)),
63 		m_microprg_ram(*this, "microprg_ram"),
64 		m_kram_page0(*this, "kram_page0"),
65 		m_kram_page1(*this, "kram_page1"),
66 		m_scsibus(*this, "scsi"),
67 		m_scsi_data_in(*this, "scsi_data_in"),
68 		m_scsi_data_out(*this, "scsi_data_out"),
69 		m_scsi_ctrl_in(*this, "scsi_ctrl_in"),
70 		m_irq_changed_cb(*this)
71 {
72 }
73 
74 
75 //-------------------------------------------------
76 //  device_validity_check - perform validity checks
77 //  on this device
78 //-------------------------------------------------
79 
device_validity_check(validity_checker & valid) const80 void huc6272_device::device_validity_check(validity_checker &valid) const
81 {
82 }
83 
84 
85 //-------------------------------------------------
86 //  device_start - device-specific startup
87 //-------------------------------------------------
88 
device_start()89 void huc6272_device::device_start()
90 {
91 	m_irq_changed_cb.resolve_safe();
92 
93 	save_item(NAME(m_register));
94 	save_item(NAME(m_kram_addr_r));
95 	save_item(NAME(m_kram_inc_r));
96 	save_item(NAME(m_kram_page_r));
97 	save_item(NAME(m_kram_addr_w));
98 	save_item(NAME(m_kram_inc_w));
99 	save_item(NAME(m_kram_page_w));
100 	save_item(NAME(m_page_setting));
101 
102 	for (int bg = 0; bg < 4; bg++)
103 	{
104 		save_item(NAME(m_bg[bg].bat_address), bg);
105 		save_item(NAME(m_bg[bg].cg_address), bg);
106 		save_item(NAME(m_bg[bg].mode), bg);
107 		save_item(NAME(m_bg[bg].height), bg);
108 		save_item(NAME(m_bg[bg].width), bg);
109 		save_item(NAME(m_bg[bg].xscroll), bg);
110 		save_item(NAME(m_bg[bg].yscroll), bg);
111 		save_item(NAME(m_bg[bg].priority), bg);
112 	}
113 
114 	save_item(NAME(m_bg0sub.bat_address));
115 	save_item(NAME(m_bg0sub.cg_address));
116 	save_item(NAME(m_bg0sub.height));
117 	save_item(NAME(m_bg0sub.width));
118 
119 	save_item(NAME(m_micro_prg.index));
120 	save_item(NAME(m_micro_prg.ctrl));
121 
122 	save_item(NAME(m_adpcm.rate));
123 	save_item(NAME(m_adpcm.status));
124 	save_item(NAME(m_adpcm.interrupt));
125 	for (int adpcm = 0; adpcm < 2; adpcm++)
126 	{
127 		save_item(NAME(m_adpcm.playing[adpcm]), adpcm);
128 		save_item(NAME(m_adpcm.control[adpcm]), adpcm);
129 		save_item(NAME(m_adpcm.start[adpcm]), adpcm);
130 		save_item(NAME(m_adpcm.end[adpcm]), adpcm);
131 		save_item(NAME(m_adpcm.imm[adpcm]), adpcm);
132 		save_item(NAME(m_adpcm.input[adpcm]), adpcm);
133 		save_item(NAME(m_adpcm.nibble[adpcm]), adpcm);
134 		save_item(NAME(m_adpcm.pos[adpcm]), adpcm);
135 		save_item(NAME(m_adpcm.addr[adpcm]), adpcm);
136 	}
137 }
138 
139 
140 //-------------------------------------------------
141 //  device_reset - device-specific reset
142 //-------------------------------------------------
143 
device_reset()144 void huc6272_device::device_reset()
145 {
146 }
147 
148 //-------------------------------------------------
149 //  memory_space_config - return a description of
150 //  any address spaces owned by this device
151 //-------------------------------------------------
152 
memory_space_config() const153 device_memory_interface::space_config_vector huc6272_device::memory_space_config() const
154 {
155 	return space_config_vector {
156 		std::make_pair(AS_PROGRAM, &m_program_space_config),
157 		std::make_pair(AS_DATA,    &m_data_space_config)
158 	};
159 }
160 
161 //**************************************************************************
162 //  INLINE HELPERS
163 //**************************************************************************
164 
165 //-------------------------------------------------
166 //  read_dword - read a dword at the given address
167 //-------------------------------------------------
168 
read_dword(offs_t address)169 inline uint32_t huc6272_device::read_dword(offs_t address)
170 {
171 	return space(AS_DATA).read_dword(address << 2);
172 }
173 
174 
175 //-------------------------------------------------
176 //  write_dword - write a dword at the given address
177 //-------------------------------------------------
178 
write_dword(offs_t address,uint32_t data)179 inline void huc6272_device::write_dword(offs_t address, uint32_t data)
180 {
181 	space(AS_DATA).write_dword(address << 2, data);
182 }
183 
write_microprg_data(offs_t address,uint16_t data)184 void huc6272_device::write_microprg_data(offs_t address, uint16_t data)
185 {
186 	space(AS_PROGRAM).write_word(address << 1, data);
187 }
188 
189 //**************************************************************************
190 //  READ/WRITE HANDLERS
191 //**************************************************************************
192 
read(offs_t offset)193 uint32_t huc6272_device::read(offs_t offset)
194 {
195 	uint32_t res = 0;
196 
197 	if((offset & 1) == 0)
198 	{
199 		/*
200 		xxxx xxxx ---- ---- ---- ---- ---- ---- Sub Channel Buffer
201 		---- ---- x--- ---- ---- ---- ---- ---- SCSI RST flag
202 		---- ---- -x-- ---- ---- ---- ---- ---- SCSI BUSY flag
203 		---- ---- --x- ---- ---- ---- ---- ---- SCSI REQ flag
204 		---- ---- ---x ---- ---- ---- ---- ---- SCSI MSG flag
205 		---- ---- ---- x--- ---- ---- ---- ---- SCSI CD flag
206 		---- ---- ---- -x-- ---- ---- ---- ---- SCSI IO flag
207 		---- ---- ---- --x- ---- ---- ---- ---- SCSI SEL flag
208 		---- ---- ---- ---- -x-- ---- ---- ---- SCSI IRQ pending
209 		---- ---- ---- ---- --x- ---- ---- ---- DMA IRQ pending
210 		---- ---- ---- ---- ---x ---- ---- ---- CD Sub Channel IRQ pending
211 		---- ---- ---- ---- ---- x--- ---- ---- Raster IRQ pending
212 		---- ---- ---- ---- ---- -x-- ---- ---- ADPCM IRQ pending
213 		---- ---- ---- ---- ---- ---- -xxx xxxx register read-back
214 		*/
215 		res = m_register & 0x7f;
216 		res |= (m_adpcm.interrupt << 10);
217 		res |= (m_scsi_ctrl_in->read() & 0xff) << 16;
218 	}
219 	else
220 	{
221 		switch(m_register)
222 		{
223 			case 0x00: // SCSI data in
224 				res = m_scsi_data_in->read() & 0xff;
225 				break;
226 
227 			case 0x05: // SCSI bus status
228 				res = m_scsi_ctrl_in->read() & 0xff;
229 				res|= (m_scsi_data_in->read() << 8);
230 				break;
231 
232 
233 			/*
234 			x--- ---- ---- ---- ----
235 			*/
236 			case 0x0c: // KRAM load address
237 				res = (m_kram_addr_r & 0x3ffff) | ((m_kram_inc_r & 0x1ff) << 18) | ((m_kram_page_r & 1) << 31);
238 				break;
239 
240 			case 0x0d: // KRAM write address
241 				res = (m_kram_addr_w & 0x3ffff) | ((m_kram_inc_w & 0x1ff) << 18) | ((m_kram_page_w & 1) << 31);
242 				break;
243 
244 			case 0x0e: // KRAM read data
245 				res = read_dword((m_kram_addr_r)|(m_kram_page_r<<18));
246 				m_kram_addr_r += (m_kram_inc_r & 0x200) ? ((m_kram_inc_r & 0x1ff) - 0x200) : (m_kram_inc_r & 0x1ff);
247 				break;
248 
249 			case 0x0f:
250 				res = m_page_setting;
251 				break;
252 
253 			case 0x53: // ADPCM status
254 				res = m_adpcm.status;
255 
256 				m_adpcm.status = 0;
257 				m_adpcm.interrupt = 0;
258 				interrupt_update();
259 				break;
260 			//default: printf("%04x\n",m_register);
261 		}
262 	}
263 
264 	return res;
265 }
266 
write(offs_t offset,uint32_t data)267 void huc6272_device::write(offs_t offset, uint32_t data)
268 {
269 	if((offset & 1) == 0)
270 		m_register = data & 0x7f;
271 	else
272 	{
273 		switch(m_register)
274 		{
275 			case 0x00: // SCSI data out
276 				m_scsi_data_out->write(data & 0xff);
277 				break;
278 			case 0x01: // SCSI command
279 				//m_scsibus->write_bsy(BIT(data, 0)); // bus?
280 				m_scsibus->write_atn(BIT(data, 1));
281 				m_scsibus->write_sel(BIT(data, 2));
282 				m_scsibus->write_ack(BIT(data, 4));
283 				m_scsibus->write_rst(BIT(data, 7));
284 
285 				break;
286 
287 			case 0x02: // SCSI mode
288 				break;
289 
290 			case 0x03: // SCSI target command
291 				m_scsibus->write_io(BIT(data, 0));
292 				m_scsibus->write_cd(BIT(data, 1));
293 				m_scsibus->write_msg(BIT(data, 2));
294 				break;
295 
296 			case 0x05: // SCSI bus status
297 				// bits 7-0: SCSI DMA trigger?
298 				m_scsi_data_out->write((data >> 8) & 0xff);
299 				break;
300 
301 			case 0x06: // SCSI input data
302 			case 0x07: // SCSI DMA trigger
303 			case 0x08: // SCSI subcode
304 			case 0x09: // SCSI DMA start address
305 			case 0x0a: // SCSI DMA size
306 			case 0x0b: // SCSI DMA control
307 				break;
308 			/*
309 			---- ---- ---- ---- ----
310 			*/
311 			case 0x0c: // KRAM load address
312 				m_kram_addr_r = (data & 0x0003ffff);
313 				m_kram_inc_r =  (data & 0x0ffc0000) >> 18;
314 				m_kram_page_r = (data & 0x80000000) >> 31;
315 				break;
316 
317 			case 0x0d: // KRAM write address
318 				m_kram_addr_w = (data & 0x0003ffff);
319 				m_kram_inc_w =  (data & 0x0ffc0000) >> 18;
320 				m_kram_page_w = (data & 0x80000000) >> 31;
321 				break;
322 
323 			case 0x0e: // KRAM write data
324 				// TODO: handle non-dword cases?
325 				write_dword((m_kram_addr_w)|(m_kram_page_w<<18),data);
326 				m_kram_addr_w += (m_kram_inc_w & 0x200) ? ((m_kram_inc_w & 0x1ff) - 0x200) : (m_kram_inc_w & 0x1ff);
327 				break;
328 
329 			/*
330 			---x ---- ---- ---- ADPCM page setting
331 			---- ---x ---- ---- RAINBOW page setting
332 			---- ---- ---x ---- BG page setting
333 			---- ---- ---- ---x SCSI page setting
334 			*/
335 			case 0x0f:
336 				m_page_setting = data;
337 				break;
338 
339 			//
340 			// xxxx ---- ---- ---- BG3 mode setting
341 			// ---- xxxx ---- ---- BG2 mode setting
342 			// ---- ---- xxxx ---- BG1 mode setting
343 			// ---- ---- ---- xxxx BG0 mode setting
344 			//
345 			// 0001 - 4 color palette
346 			// 0010 - 16 color palette
347 			// 0011 - 256 color palette
348 			// 0100 - 64k color
349 			// 0101 - 16M color
350 			// 1001 - 4 color palette block mode
351 			// 1010 - 16 color palette block mode
352 			// 1011 - 256 color palette block mode
353 			// others - unused/invalid
354 			case 0x10:
355 				for(int i=0;i<4;i++)
356 					m_bg[i].mode = (data >> i*4) & 0x0f;
357 
358 				break;
359 
360 			/*
361 			---x ---- ---- ---- BG0 rotation enable
362 			---- xxx- ---- ---- BG3 priority
363 			---- ---x xx-- ---- BG2 priority
364 			---- ---- --xx x--- BG1 priority
365 			---- ---- ---- -xxx BG0 priority
366 			*/
367 			case 0x12:
368 				for(int i=0;i<4;i++)
369 					m_bg[i].priority = (data >> i*3) & 0x07;
370 
371 				// TODO: rotation enable
372 				break;
373 
374 			case 0x13:
375 				m_micro_prg.index = data & 0xf;
376 				break;
377 
378 			case 0x14:
379 				write_microprg_data(m_micro_prg.index,data & 0xffff);
380 				m_micro_prg.index ++;
381 				m_micro_prg.index &= 0xf;
382 				break;
383 
384 			case 0x15:
385 				m_micro_prg.ctrl = data & 1;
386 
387 				break;
388 
389 			// case 0x16: wrap-around enable
390 
391 			// BAT and CG address setters
392 			case 0x20: m_bg[0].bat_address = data * 1024;  break;
393 			case 0x21: m_bg[0].cg_address = data * 1024;   break;
394 			case 0x22: m_bg0sub.bat_address = data * 1024; break;
395 			case 0x23: m_bg0sub.cg_address = data * 1024;  break;
396 			case 0x24: m_bg[1].bat_address = data * 1024;  break;
397 			case 0x25: m_bg[1].cg_address = data * 1024;   break;
398 			case 0x26: m_bg[2].bat_address = data * 1024;  break;
399 			case 0x27: m_bg[2].cg_address = data * 1024;   break;
400 			case 0x28: m_bg[3].bat_address = data * 1024;  break;
401 			case 0x29: m_bg[3].cg_address = data * 1024;   break;
402 
403 			// Height & Width setters
404 			case 0x2c:
405 			case 0x2d:
406 			case 0x2e:
407 			case 0x2f:
408 			{
409 				uint8_t reg_offs = m_register & 3;
410 				m_bg[reg_offs].height = 1 << (data & 0x000f);
411 				m_bg[reg_offs].width = 1 << ((data & 0x00f0) >> 4);
412 				if(reg_offs == 0)
413 				{
414 					m_bg0sub.height = 1 << ((data & 0x0f00) >> 8);
415 					m_bg0sub.width = 1 << ((data & 0xf000) >> 12);
416 				}
417 				break;
418 			}
419 
420 			// X & Y scroll values
421 			case 0x30:
422 			case 0x31:
423 			case 0x32:
424 			case 0x33:
425 			case 0x34:
426 			case 0x35:
427 			case 0x36:
428 			case 0x37:
429 			{
430 				uint8_t reg_offs = (m_register & 6) >> 1;
431 
432 				if(m_register & 1)
433 					m_bg[reg_offs].yscroll = data & 0xffff;
434 				else
435 					m_bg[reg_offs].xscroll = data & 0xffff;
436 				break;
437 			}
438 
439 			case 0x50: // ADPCM control
440 			{
441 				for (int i = 0; i < 2; i++)
442 				{
443 					m_adpcm.playing[i] = BIT(data, i);
444 					if (!m_adpcm.playing[i])
445 					{
446 						m_adpcm.input[i] = -1;
447 						m_adpcm.pos[i] = 0;
448 					}
449 					else
450 					{
451 						m_adpcm.addr[i] = m_adpcm.start[i];
452 					}
453 				}
454 
455 				m_adpcm.rate = (data & 0xc) >> 2;
456 				break;
457 			}
458 
459 			// ADPCM channel control
460 			case 0x51:
461 			case 0x52:
462 			{
463 				uint8_t reg_offs = 1-(m_register & 1);
464 				m_adpcm.control[reg_offs] = data & 0x7;
465 				if (BIT(m_adpcm.control[reg_offs], 1) == 0)
466 					m_adpcm.status &= ~(1 << (2*reg_offs));
467 
468 				if (BIT(m_adpcm.control[reg_offs], 2) == 0)
469 					m_adpcm.status &= ~(1 << (2*reg_offs+1));
470 
471 				break;
472 			}
473 
474 			// ADPCM start address
475 			case 0x58:
476 			case 0x5c:
477 				m_adpcm.start[(m_register >> 2) & 1] = (data << 8) & 0x3ffff;
478 				break;
479 
480 			// ADPCM end address
481 			case 0x59:
482 			case 0x5d:
483 				m_adpcm.end[(m_register >> 2) & 1] = data & 0x3ffff;
484 				break;
485 
486 			// ADPCM intermediate address
487 			case 0x5a:
488 			case 0x5e:
489 				m_adpcm.imm[(m_register >> 2) & 1] = (data << 6) & 0x3ffff;
490 				break;
491 
492 			//default: printf("%04x %04x %08x\n",m_register,data,mem_mask);
493 		}
494 	}
495 }
496 
adpcm_update(int chan)497 uint8_t huc6272_device::adpcm_update(int chan)
498 {
499 	if (!m_adpcm.playing[chan])
500 		return 0;
501 
502 	const unsigned rate = (1 << m_adpcm.rate);
503 	m_adpcm.pos[chan]++;
504 	if (m_adpcm.pos[chan] >= rate)
505 	{
506 		if (m_adpcm.input[chan] == -1)
507 		{
508 			m_adpcm.input[chan] = read_dword(((m_page_setting & 0x1000) << 6) | m_adpcm.addr[chan]);
509 			m_adpcm.addr[chan] = (m_adpcm.addr[chan] & 0x20000) | ((m_adpcm.addr[chan] + 1) & 0x1ffff);
510 			if (m_adpcm.addr[chan] == m_adpcm.imm[chan])
511 			{
512 				m_adpcm.status |= (1 << (chan*2+1));
513 				if (BIT(m_adpcm.control[chan], 2))
514 				{
515 					m_adpcm.interrupt = 1;
516 					interrupt_update();
517 				}
518 			}
519 			if (m_adpcm.addr[chan] > m_adpcm.end[chan])
520 			{
521 				m_adpcm.status |= (1 << (chan*2));
522 				if (BIT(m_adpcm.control[chan], 1))
523 				{
524 					m_adpcm.interrupt = 1;
525 					interrupt_update();
526 				}
527 
528 				if (BIT(m_adpcm.control[chan],0)) // Ring Buffer
529 				{
530 					m_adpcm.addr[chan] = m_adpcm.start[chan];
531 				}
532 				else
533 				{
534 					m_adpcm.playing[chan] = 0;
535 					return 0;
536 				}
537 			}
538 			m_adpcm.nibble[chan] = 0;
539 		}
540 		else
541 		{
542 			m_adpcm.nibble[chan] += 4;
543 			if (m_adpcm.nibble[chan] >= 28)
544 				m_adpcm.input[chan] = -1;
545 		}
546 		m_adpcm.pos[chan] = 0;
547 	}
548 
549 	return (m_adpcm.input[chan] >> m_adpcm.nibble[chan]) & 0xf;
550 }
551 
adpcm_update_0()552 uint8_t huc6272_device::adpcm_update_0()
553 {
554 	return adpcm_update(0);
555 }
556 
adpcm_update_1()557 uint8_t huc6272_device::adpcm_update_1()
558 {
559 	return adpcm_update(1);
560 }
561 
cdda_update(offs_t offset,uint8_t data)562 void huc6272_device::cdda_update(offs_t offset, uint8_t data)
563 {
564 	if (offset)
565 		m_cdda_r->set_output_gain(ALL_OUTPUTS, float(data) / 63.0);
566 	else
567 		m_cdda_l->set_output_gain(ALL_OUTPUTS, float(data) / 63.0);
568 }
569 
interrupt_update()570 void huc6272_device::interrupt_update()
571 {
572 	if (m_adpcm.interrupt)
573 		m_irq_changed_cb(ASSERT_LINE);
574 	else
575 		m_irq_changed_cb(CLEAR_LINE);
576 }
577 
cdrom_config(device_t * device)578 void huc6272_device::cdrom_config(device_t *device)
579 {
580 	cdda_device *cdda = device->subdevice<cdda_device>("cdda");
581 	cdda->add_route(0, "^^cdda_l", 1.0);
582 	cdda->add_route(1, "^^cdda_r", 1.0);
583 }
584 
585 //-------------------------------------------------
586 //  device_add_mconfig - add device configuration
587 //-------------------------------------------------
588 
device_add_mconfig(machine_config & config)589 void huc6272_device::device_add_mconfig(machine_config &config)
590 {
591 	SPEAKER(config, m_cdda_l).front_left();
592 	SPEAKER(config, m_cdda_r).front_right();
593 
594 	scsi_port_device &scsibus(SCSI_PORT(config, "scsi"));
595 	scsibus.set_data_input_buffer("scsi_data_in");
596 	scsibus.rst_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit7));
597 	scsibus.bsy_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit6));
598 	scsibus.req_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit5));
599 	scsibus.msg_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit4));
600 	scsibus.cd_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit3));
601 	scsibus.io_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit2));
602 	scsibus.sel_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit1));
603 
604 	output_latch_device &scsiout(OUTPUT_LATCH(config, "scsi_data_out"));
605 	scsibus.set_output_latch(scsiout);
606 
607 	INPUT_BUFFER(config, "scsi_ctrl_in");
608 	INPUT_BUFFER(config, "scsi_data_in");
609 
610 	scsibus.set_slot_device(1, "cdrom", SCSICD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_1));
611 	scsibus.slot(1).set_option_machine_config("cdrom", cdrom_config);
612 }
613