summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/huc6272.cpp
blob: b091b67afb9c2b0ce26953836e2d4b27d7fa0454 (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                               



                                                                            

                                      
                                
 


                                                 


                                                                            
 








                                                                            
                                                                                 
 

                                                   

                                                            



                                               




                                                                  
 

 



                                                                            
                                                   
                                

                                                   
                                                                                                               
                                                        
                                                        
                                                         

                                          

                                                                                                                                                   







                                                        
 

 

                                                   

                                                   

                                                   
                                                                         
 



                                                   
                                          

                                                   
                                   
 
                                        












































                                                               
 
 

                                                   
                                        

                                                   
                                   
 

 
                                                   

                                                 

                                                   
                                                                                        
 
                                    


                                                                    

 


                                                                            

                                                   
                                                  

                                                   
                                                          
 
                                                       



                                                   
                                                    

                                                   
                                                                      
 
                                                       

 



                                                                       




                                                                            
                                     
 
                         

                             

                  














                                                                                  
                                        
                                                 
                                                             
         



                                  


                                                                    
 



                                                                    

 
                          

                                                







                                                                                                                               
                                                    
                                                                                      
                                                                                                                                    
                                      



                                                     







                                                      




                                                               

 
                                       
 


                                         
         

                                  


                                                                    
                                                  




                                                                             
 
                                      
 
                                               

                                      
                                                         



                                                                   
 
                                                     


                                                                           
 





                                                            
                                      
                          

                                                

                                                                    
                                                                          




                                                                          
                                                                          


                                                                          

                                                                
                                                                                      
                                                                                                                                    

                                      
                          




                                                                



                                                      















                                                               


                                                                            

                                      












                                                                                
 
                                  
                                                               


                                      


                                                                                     



                                                            
 
                                      
 
                                                        
 












                                                                             

                                  












                                                                                      
 

















                                                                               
 




















































                                                                                             
                                                                                       

                 
 
 































































                                                                                                                   







                                                                           







                                              

                                                   


                                                                   

 



                                                   

                                                               


                                                










                                                                                         
                                          




                                                                                           
                                                                         
 
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol, Angelo Salese
/***************************************************************************

    Hudson/NEC HuC6272 "King" device

    TODO:
    - Use NSCSI instead of legacy one!
    - ADPCM Transfer is correct?

    ADPCM related patents:
    - https://patents.google.com/patent/US5692099

***************************************************************************/

#include "emu.h"

#include "video/huc6272.h"



//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(HUC6272, huc6272_device, "huc6272", "Hudson HuC6272 \"King\"")

void huc6272_device::microprg_map(address_map &map)
{
	if (!has_configured_map(0))
		map(0x00, 0x0f).ram().share("microprg_ram");
}

void huc6272_device::kram_map(address_map &map)
{
	if (!has_configured_map(1))
	{
		map(0x000000, 0x0fffff).ram().share("kram_page0");
		map(0x100000, 0x1fffff).ram().share("kram_page1");
	}
}


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  huc6272_device - constructor
//-------------------------------------------------

huc6272_device::huc6272_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, HUC6272, tag, owner, clock),
		device_memory_interface(mconfig, *this),
		m_huc6271(*this, finder_base::DUMMY_TAG),
		m_cdda_l(*this, "cdda_l"),
		m_cdda_r(*this, "cdda_r"),
		m_program_space_config("microprg", ENDIANNESS_LITTLE, 16, 4, 0, address_map_constructor(FUNC(huc6272_device::microprg_map), this)),
		m_data_space_config("kram", ENDIANNESS_LITTLE, 32, 21, 0, address_map_constructor(FUNC(huc6272_device::kram_map), this)),
		m_microprg_ram(*this, "microprg_ram"),
		m_kram_page0(*this, "kram_page0"),
		m_kram_page1(*this, "kram_page1"),
		m_scsibus(*this, "scsi"),
		m_scsi_data_in(*this, "scsi_data_in"),
		m_scsi_data_out(*this, "scsi_data_out"),
		m_scsi_ctrl_in(*this, "scsi_ctrl_in"),
		m_irq_changed_cb(*this)
{
}


//-------------------------------------------------
//  device_validity_check - perform validity checks
//  on this device
//-------------------------------------------------

void huc6272_device::device_validity_check(validity_checker &valid) const
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void huc6272_device::device_start()
{
	m_irq_changed_cb.resolve_safe();

	save_item(NAME(m_register));
	save_item(NAME(m_kram_addr_r));
	save_item(NAME(m_kram_inc_r));
	save_item(NAME(m_kram_page_r));
	save_item(NAME(m_kram_addr_w));
	save_item(NAME(m_kram_inc_w));
	save_item(NAME(m_kram_page_w));
	save_item(NAME(m_page_setting));

	for (int bg = 0; bg < 4; bg++)
	{
		save_item(NAME(m_bg[bg].bat_address), bg);
		save_item(NAME(m_bg[bg].cg_address), bg);
		save_item(NAME(m_bg[bg].mode), bg);
		save_item(NAME(m_bg[bg].height), bg);
		save_item(NAME(m_bg[bg].width), bg);
		save_item(NAME(m_bg[bg].xscroll), bg);
		save_item(NAME(m_bg[bg].yscroll), bg);
		save_item(NAME(m_bg[bg].priority), bg);
	}

	save_item(NAME(m_bg0sub.bat_address));
	save_item(NAME(m_bg0sub.cg_address));
	save_item(NAME(m_bg0sub.height));
	save_item(NAME(m_bg0sub.width));

	save_item(NAME(m_micro_prg.index));
	save_item(NAME(m_micro_prg.ctrl));

	save_item(NAME(m_adpcm.rate));
	save_item(NAME(m_adpcm.status));
	save_item(NAME(m_adpcm.interrupt));
	for (int adpcm = 0; adpcm < 2; adpcm++)
	{
		save_item(NAME(m_adpcm.playing[adpcm]), adpcm);
		save_item(NAME(m_adpcm.control[adpcm]), adpcm);
		save_item(NAME(m_adpcm.start[adpcm]), adpcm);
		save_item(NAME(m_adpcm.end[adpcm]), adpcm);
		save_item(NAME(m_adpcm.imm[adpcm]), adpcm);
		save_item(NAME(m_adpcm.input[adpcm]), adpcm);
		save_item(NAME(m_adpcm.nibble[adpcm]), adpcm);
		save_item(NAME(m_adpcm.pos[adpcm]), adpcm);
		save_item(NAME(m_adpcm.addr[adpcm]), adpcm);
	}
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void huc6272_device::device_reset()
{
}

//-------------------------------------------------
//  memory_space_config - return a description of
//  any address spaces owned by this device
//-------------------------------------------------

device_memory_interface::space_config_vector huc6272_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(AS_PROGRAM, &m_program_space_config),
		std::make_pair(AS_DATA,    &m_data_space_config)
	};
}

//**************************************************************************
//  INLINE HELPERS
//**************************************************************************

//-------------------------------------------------
//  read_dword - read a dword at the given address
//-------------------------------------------------

inline uint32_t huc6272_device::read_dword(offs_t address)
{
	return space(AS_DATA).read_dword(address << 2);
}


//-------------------------------------------------
//  write_dword - write a dword at the given address
//-------------------------------------------------

inline void huc6272_device::write_dword(offs_t address, uint32_t data)
{
	space(AS_DATA).write_dword(address << 2, data);
}

void huc6272_device::write_microprg_data(offs_t address, uint16_t data)
{
	space(AS_PROGRAM).write_word(address << 1, data);
}

//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

READ32_MEMBER( huc6272_device::read )
{
	uint32_t res = 0;

	if((offset & 1) == 0)
	{
		/*
		xxxx xxxx ---- ---- ---- ---- ---- ---- Sub Channel Buffer
		---- ---- x--- ---- ---- ---- ---- ---- SCSI RST flag
		---- ---- -x-- ---- ---- ---- ---- ---- SCSI BUSY flag
		---- ---- --x- ---- ---- ---- ---- ---- SCSI REQ flag
		---- ---- ---x ---- ---- ---- ---- ---- SCSI MSG flag
		---- ---- ---- x--- ---- ---- ---- ---- SCSI CD flag
		---- ---- ---- -x-- ---- ---- ---- ---- SCSI IO flag
		---- ---- ---- --x- ---- ---- ---- ---- SCSI SEL flag
		---- ---- ---- ---- -x-- ---- ---- ---- SCSI IRQ pending
		---- ---- ---- ---- --x- ---- ---- ---- DMA IRQ pending
		---- ---- ---- ---- ---x ---- ---- ---- CD Sub Channel IRQ pending
		---- ---- ---- ---- ---- x--- ---- ---- Raster IRQ pending
		---- ---- ---- ---- ---- -x-- ---- ---- ADPCM IRQ pending
		---- ---- ---- ---- ---- ---- -xxx xxxx register read-back
		*/
		res = m_register & 0x7f;
		res |= (m_adpcm.interrupt << 10);
		res |= (m_scsi_ctrl_in->read() & 0xff) << 16;
	}
	else
	{
		switch(m_register)
		{
			case 0x00: // SCSI data in
				res = m_scsi_data_in->read() & 0xff;
				break;

			case 0x05: // SCSI bus status
				res = m_scsi_ctrl_in->read() & 0xff;
				res|= (m_scsi_data_in->read() << 8);
				break;


			/*
			x--- ---- ---- ---- ----
			*/
			case 0x0c: // KRAM load address
				res = (m_kram_addr_r & 0x3ffff) | ((m_kram_inc_r & 0x1ff) << 18) | ((m_kram_page_r & 1) << 31);
				break;

			case 0x0d: // KRAM write address
				res = (m_kram_addr_w & 0x3ffff) | ((m_kram_inc_w & 0x1ff) << 18) | ((m_kram_page_w & 1) << 31);
				break;

			case 0x0e: // KRAM read data
				res = read_dword((m_kram_addr_r)|(m_kram_page_r<<18));
				m_kram_addr_r += (m_kram_inc_r & 0x200) ? ((m_kram_inc_r & 0x1ff) - 0x200) : (m_kram_inc_r & 0x1ff);
				break;

			case 0x0f:
				res = m_page_setting;
				break;

			case 0x53: // ADPCM status
				res = m_adpcm.status;

				m_adpcm.status = 0;
				m_adpcm.interrupt = 0;
				interrupt_update();
				break;
			//default: printf("%04x\n",m_register);
		}
	}

	return res;
}

WRITE32_MEMBER( huc6272_device::write )
{
	if((offset & 1) == 0)
		m_register = data & 0x7f;
	else
	{
		switch(m_register)
		{
			case 0x00: // SCSI data out
				m_scsi_data_out->write(data & 0xff);
				break;
			case 0x01: // SCSI command
				//m_scsibus->write_bsy(BIT(data, 0)); // bus?
				m_scsibus->write_atn(BIT(data, 1));
				m_scsibus->write_sel(BIT(data, 2));
				m_scsibus->write_ack(BIT(data, 4));
				m_scsibus->write_rst(BIT(data, 7));

				break;

			case 0x02: // SCSI mode
				break;

			case 0x03: // SCSI target command
				m_scsibus->write_io(BIT(data, 0));
				m_scsibus->write_cd(BIT(data, 1));
				m_scsibus->write_msg(BIT(data, 2));
				break;

			case 0x05: // SCSI bus status
				// bits 7-0: SCSI DMA trigger?
				m_scsi_data_out->write((data >> 8) & 0xff);
				break;

			case 0x06: // SCSI input data
			case 0x07: // SCSI DMA trigger
			case 0x08: // SCSI subcode
			case 0x09: // SCSI DMA start address
			case 0x0a: // SCSI DMA size
			case 0x0b: // SCSI DMA control
				break;
			/*
			---- ---- ---- ---- ----
			*/
			case 0x0c: // KRAM load address
				m_kram_addr_r = (data & 0x0003ffff);
				m_kram_inc_r =  (data & 0x0ffc0000) >> 18;
				m_kram_page_r = (data & 0x80000000) >> 31;
				break;

			case 0x0d: // KRAM write address
				m_kram_addr_w = (data & 0x0003ffff);
				m_kram_inc_w =  (data & 0x0ffc0000) >> 18;
				m_kram_page_w = (data & 0x80000000) >> 31;
				break;

			case 0x0e: // KRAM write data
				// TODO: handle non-dword cases?
				write_dword((m_kram_addr_w)|(m_kram_page_w<<18),data);
				m_kram_addr_w += (m_kram_inc_w & 0x200) ? ((m_kram_inc_w & 0x1ff) - 0x200) : (m_kram_inc_w & 0x1ff);
				break;

			/*
			---x ---- ---- ---- ADPCM page setting
			---- ---x ---- ---- RAINBOW page setting
			---- ---- ---x ---- BG page setting
			---- ---- ---- ---x SCSI page setting
			*/
			case 0x0f:
				m_page_setting = data;
				break;

			//
			// xxxx ---- ---- ---- BG3 mode setting
			// ---- xxxx ---- ---- BG2 mode setting
			// ---- ---- xxxx ---- BG1 mode setting
			// ---- ---- ---- xxxx BG0 mode setting
			//
			// 0001 - 4 color palette
			// 0010 - 16 color palette
			// 0011 - 256 color palette
			// 0100 - 64k color
			// 0101 - 16M color
			// 1001 - 4 color palette block mode
			// 1010 - 16 color palette block mode
			// 1011 - 256 color palette block mode
			// others - unused/invalid
			case 0x10:
				for(int i=0;i<4;i++)
					m_bg[i].mode = (data >> i*4) & 0x0f;

				break;

			/*
			---x ---- ---- ---- BG0 rotation enable
			---- xxx- ---- ---- BG3 priority
			---- ---x xx-- ---- BG2 priority
			---- ---- --xx x--- BG1 priority
			---- ---- ---- -xxx BG0 priority
			*/
			case 0x12:
				for(int i=0;i<4;i++)
					m_bg[i].priority = (data >> i*3) & 0x07;

				// TODO: rotation enable
				break;

			case 0x13:
				m_micro_prg.index = data & 0xf;
				break;

			case 0x14:
				write_microprg_data(m_micro_prg.index,data & 0xffff);
				m_micro_prg.index ++;
				m_micro_prg.index &= 0xf;
				break;

			case 0x15:
				m_micro_prg.ctrl = data & 1;

				break;

			// case 0x16: wrap-around enable

			// BAT and CG address setters
			case 0x20: m_bg[0].bat_address = data * 1024;  break;
			case 0x21: m_bg[0].cg_address = data * 1024;   break;
			case 0x22: m_bg0sub.bat_address = data * 1024; break;
			case 0x23: m_bg0sub.cg_address = data * 1024;  break;
			case 0x24: m_bg[1].bat_address = data * 1024;  break;
			case 0x25: m_bg[1].cg_address = data * 1024;   break;
			case 0x26: m_bg[2].bat_address = data * 1024;  break;
			case 0x27: m_bg[2].cg_address = data * 1024;   break;
			case 0x28: m_bg[3].bat_address = data * 1024;  break;
			case 0x29: m_bg[3].cg_address = data * 1024;   break;

			// Height & Width setters
			case 0x2c:
			case 0x2d:
			case 0x2e:
			case 0x2f:
			{
				uint8_t reg_offs = m_register & 3;
				m_bg[reg_offs].height = 1 << (data & 0x000f);
				m_bg[reg_offs].width = 1 << ((data & 0x00f0) >> 4);
				if(reg_offs == 0)
				{
					m_bg0sub.height = 1 << ((data & 0x0f00) >> 8);
					m_bg0sub.width = 1 << ((data & 0xf000) >> 12);
				}
				break;
			}

			// X & Y scroll values
			case 0x30:
			case 0x31:
			case 0x32:
			case 0x33:
			case 0x34:
			case 0x35:
			case 0x36:
			case 0x37:
			{
				uint8_t reg_offs = (m_register & 6) >> 1;

				if(m_register & 1)
					m_bg[reg_offs].yscroll = data & 0xffff;
				else
					m_bg[reg_offs].xscroll = data & 0xffff;
				break;
			}

			case 0x50: // ADPCM control
			{
				for (int i = 0; i < 2; i++)
				{
					m_adpcm.playing[i] = BIT(data, i);
					if (!m_adpcm.playing[i])
					{
						m_adpcm.input[i] = -1;
						m_adpcm.pos[i] = 0;
					}
					else
					{
						m_adpcm.addr[i] = m_adpcm.start[i];
					}
				}

				m_adpcm.rate = (data & 0xc) >> 2;
				break;
			}

			// ADPCM channel control
			case 0x51:
			case 0x52:
			{
				uint8_t reg_offs = 1-(m_register & 1);
				m_adpcm.control[reg_offs] = data & 0x7;
				if (BIT(m_adpcm.control[reg_offs], 1) == 0)
					m_adpcm.status &= ~(1 << (2*reg_offs));

				if (BIT(m_adpcm.control[reg_offs], 2) == 0)
					m_adpcm.status &= ~(1 << (2*reg_offs+1));

				break;
			}

			// ADPCM start address
			case 0x58:
			case 0x5c:
				m_adpcm.start[(m_register >> 2) & 1] = (data << 8) & 0x3ffff;
				break;

			// ADPCM end address
			case 0x59:
			case 0x5d:
				m_adpcm.end[(m_register >> 2) & 1] = data & 0x3ffff;
				break;

			// ADPCM intermediate address
			case 0x5a:
			case 0x5e:
				m_adpcm.imm[(m_register >> 2) & 1] = (data << 6) & 0x3ffff;
				break;

			//default: printf("%04x %04x %08x\n",m_register,data,mem_mask);
		}
	}
}

uint8_t huc6272_device::adpcm_update(int chan)
{
	if (!m_adpcm.playing[chan])
		return 0;

	int rate = (1 << m_adpcm.rate);
	m_adpcm.pos[chan]++;
	if (m_adpcm.pos[chan] > rate)
	{
		if (m_adpcm.input[chan] == -1)
		{
			m_adpcm.input[chan] = read_dword(((m_page_setting & 0x1000) << 6) | m_adpcm.addr[chan]);
			m_adpcm.addr[chan] = (m_adpcm.addr[chan] & 0x20000) | ((m_adpcm.addr[chan] + 1) & 0x1ffff);
			if (m_adpcm.addr[chan] == m_adpcm.imm[chan])
			{
				m_adpcm.status |= (1 << (chan*2+1));
				if (BIT(m_adpcm.control[chan], 2))
				{
					m_adpcm.interrupt = 1;
					interrupt_update();
				}
			}
			if (m_adpcm.addr[chan] > m_adpcm.end[chan])
			{
				m_adpcm.status |= (1 << (chan*2));
				if (BIT(m_adpcm.control[chan], 1))
				{
					m_adpcm.interrupt = 1;
					interrupt_update();
				}

				if (BIT(m_adpcm.control[chan],0)) // Ring Buffer
				{
					m_adpcm.addr[chan] = m_adpcm.start[chan];
				}
				else
				{
					m_adpcm.playing[chan] = 0;
					return 0;
				}
			}
			m_adpcm.nibble[chan] = 0;
		}
		else
		{
			m_adpcm.nibble[chan] += 4;
			if (m_adpcm.nibble[chan] >= 28)
				m_adpcm.input[chan] = -1;
		}
	}

	return (m_adpcm.input[chan] >> m_adpcm.nibble[chan]) & 0xf;
}

READ8_MEMBER(huc6272_device::adpcm_update_0)
{
	return adpcm_update(0);
}

READ8_MEMBER(huc6272_device::adpcm_update_1)
{
	return adpcm_update(1);
}

WRITE8_MEMBER(huc6272_device::cdda_update)
{
	if (offset)
		m_cdda_r->set_output_gain(ALL_OUTPUTS, float(data) / 63.0);
	else
		m_cdda_l->set_output_gain(ALL_OUTPUTS, float(data) / 63.0);
}

void huc6272_device::interrupt_update()
{
	if (m_adpcm.interrupt)
		m_irq_changed_cb(ASSERT_LINE);
	else
		m_irq_changed_cb(CLEAR_LINE);
}

void huc6272_device::cdrom_config(device_t *device)
{
	cdda_device *cdda = device->subdevice<cdda_device>("cdda");
	cdda->add_route(0, "^^cdda_l", 1.0);
	cdda->add_route(1, "^^cdda_r", 1.0);
}

//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

void huc6272_device::device_add_mconfig(machine_config &config)
{
	SPEAKER(config, m_cdda_l).front_left();
	SPEAKER(config, m_cdda_r).front_right();

	scsi_port_device &scsibus(SCSI_PORT(config, "scsi"));
	scsibus.set_data_input_buffer("scsi_data_in");
	scsibus.rst_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit7));
	scsibus.bsy_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit6));
	scsibus.req_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit5));
	scsibus.msg_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit4));
	scsibus.cd_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit3));
	scsibus.io_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit2));
	scsibus.sel_handler().set("scsi_ctrl_in", FUNC(input_buffer_device::write_bit1));

	output_latch_device &scsiout(OUTPUT_LATCH(config, "scsi_data_out"));
	scsibus.set_output_latch(scsiout);

	INPUT_BUFFER(config, "scsi_ctrl_in");
	INPUT_BUFFER(config, "scsi_data_in");

	scsibus.set_slot_device(1, "cdrom", SCSICD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_1));
	scsibus.slot(1).set_option_machine_config("cdrom", cdrom_config);
}