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