mame/src/devices/machine/iopsio2.cpp

310 lines
8.4 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/******************************************************************************
*
* Sony PlayStation 2 IOP SIO2 device skeleton
*
* To Do:
* Everything
*
*/
#include "iopsio2.h"
DEFINE_DEVICE_TYPE(SONYIOP_SIO2, iop_sio2_device, "iopsio2", "PlayStation 2 IOP SIO2")
/*static*/ const size_t iop_sio2_device::BUFFER_SIZE = 512; // total guess based on memcard block size
iop_sio2_device::iop_sio2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, SONYIOP_SIO2, tag, owner, clock)
, m_intc(*this, finder_base::DUMMY_TAG)
, m_pad0(*this, finder_base::DUMMY_TAG)
, m_pad1(*this, finder_base::DUMMY_TAG)
, m_mc0(*this, finder_base::DUMMY_TAG)
{
}
void iop_sio2_device::device_start()
{
if (!m_response_timer)
m_response_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(iop_sio2_device::response_timer), this));
save_item(NAME(m_buffer));
save_item(NAME(m_curr_byte));
save_item(NAME(m_end_byte));
save_item(NAME(m_ctrl));
save_item(NAME(m_unknown_0x6c));
save_item(NAME(m_unknown_0x70));
save_item(NAME(m_unknown_0x74));
save_item(NAME(m_cmdbuf));
save_item(NAME(m_curr_port));
save_item(NAME(m_cmd_size));
save_item(NAME(m_cmd_length));
save_item(NAME(m_databuf[0]));
save_item(NAME(m_databuf[1]));
save_item(NAME(m_target_device));
}
void iop_sio2_device::device_reset()
{
memset(m_buffer, 0, BUFFER_SIZE);
m_curr_byte = 0;
m_end_byte = 0;
m_ctrl = 0;
m_unknown_0x6c = 0;
m_unknown_0x70 = 0xf;
m_unknown_0x74 = 0;
memset(m_cmdbuf, 0, sizeof(uint32_t) * 16);
m_curr_port = 0;
m_cmd_size = 0;
m_cmd_length = 0;
memset(m_databuf, 0, sizeof(uint32_t) * 4 * 2);
m_target_device = 0;
}
READ32_MEMBER(iop_sio2_device::read)
{
uint32_t ret = 0;
switch (offset)
{
case 0x64/4:
ret = receive();
//logerror("%s: read: FIFO RECV %08x & %08x\n", machine().describe_context(), ret, mem_mask);
break;
case 0x68/4: // Control?
ret = m_ctrl;
//logerror("%s: read: CTRL %08x & %08x\n", machine().describe_context(), ret, mem_mask);
break;
case 0x6c/4:
ret = m_unknown_0x6c;
//logerror("%s: read: Unknown 0x6c (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
break;
case 0x70/4:
ret = m_unknown_0x70;
//logerror("%s: read: Unknown 0x70 (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
break;
case 0x74/4:
ret = m_unknown_0x74;
//logerror("%s: read: Unknown 0x74 (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
break;
case 0x80/4:
// Unknown. Case is here to prevent log spam.
break;
default:
logerror("%s: read: Unknown offset %08x & %08x\n", machine().describe_context(), 0x1f808200 + (offset << 2), mem_mask);
break;
}
return ret;
}
WRITE32_MEMBER(iop_sio2_device::write)
{
switch (offset)
{
case 0x00/4: case 0x04/4: case 0x08/4: case 0x0c/4: case 0x10/4: case 0x14/4: case 0x18/4: case 0x1c/4:
case 0x20/4: case 0x24/4: case 0x28/4: case 0x2c/4: case 0x30/4: case 0x34/4: case 0x38/4: case 0x3c/4:
//logerror("%s: write: CMDBUF[%d] = %08x & %08x\n", machine().describe_context(), offset, data, mem_mask);
COMBINE_DATA(&m_cmdbuf[offset]);
break;
case 0x40/4: case 0x44/4:
//logerror("%s: write: DATABUF[0][%d] = %08x & %08x\n", machine().describe_context(), offset - 0x40/4, data, mem_mask);
COMBINE_DATA(&m_databuf[0][offset - 0x40/4]);
break;
case 0x48/4: case 0x4c/4:
//logerror("%s: write: DATABUF[1][%d] = %08x & %08x\n", machine().describe_context(), offset - 0x48/4, data, mem_mask);
COMBINE_DATA(&m_databuf[1][offset - 0x48/4]);
break;
case 0x50/4: case 0x54/4:
//logerror("%s: write: DATABUF[2][%d] = %08x & %08x\n", machine().describe_context(), offset - 0x50/4, data, mem_mask);
COMBINE_DATA(&m_databuf[2][offset - 0x50/4]);
break;
case 0x58/4: case 0x5c/4:
//logerror("%s: write: DATABUF[3][%d] = %08x & %08x\n", machine().describe_context(), offset - 0x58/4, data, mem_mask);
COMBINE_DATA(&m_databuf[3][offset - 0x58/4]);
break;
case 0x60/4:
//logerror("%s: write: XMIT %08x & %08x\n", machine().describe_context(), data, mem_mask);
transmit_to_device_hack((uint8_t)data);
break;
case 0x68/4: // Control?
{
//logerror("%s: write: Control? %08x & %08x\n", machine().describe_context(), data, mem_mask);
//uint32_t old_ctrl = m_ctrl;
m_ctrl = data & ~CTRL_IRQ;
if (data & CTRL_IRQ)
{
m_intc->raise_interrupt(iop_intc_device::INT_SIO2);
}
else
{
m_curr_port = 0;
m_cmd_size = 0;
m_cmd_length = 0;
m_end_byte = 0;
m_curr_byte = 0;
m_target_device = 0;
}
break;
}
case 0x80/4:
// Unknown. Case is here to prevent log spam.
break;
default:
logerror("%s: write: Unknown offset %08x = %08x & %08x\n", machine().describe_context(), 0x1f808200 + (offset << 2), data, mem_mask);
break;
}
}
uint8_t iop_sio2_device::receive()
{
if (m_curr_byte >= m_end_byte)
{
//logerror("D:%d == E:%d, returning 0\n", m_curr_byte, m_end_byte);
return 0;
}
const uint8_t ret = m_buffer[m_curr_byte];
//logerror("buf[%d] = %02x\n", m_curr_byte, ret);
m_curr_byte++;
if (m_curr_byte >= m_end_byte)
{
//logerror("Reached end of buffer, resetting to 0\n");
m_curr_byte = 0;
m_end_byte = 0;
}
return ret;
}
void iop_sio2_device::transmit(uint8_t data)
{
//logerror("%s: Transmitting: %02x\n", machine().describe_context(), data);
if (!m_cmd_size && m_cmdbuf[m_curr_port])
{
m_cmd_size = (m_cmdbuf[m_curr_port++] & 0x0001ff00) >> 8;
m_cmd_length = m_cmd_size;
}
// TODO: Transmit serially to actual slot device at 250kHz-2MHz as needed
transmit_to_device_hack(data);
}
ps2_pad_device* iop_sio2_device::pad(uint8_t port)
{
return (port == 0) ? m_pad0.target() : m_pad1.target();
}
TIMER_CALLBACK_MEMBER(iop_sio2_device::response_timer)
{
if (param < 2)
{
pad(param)->process_fifos();
while (pad(param)->xmit_fifo_depth() && m_end_byte < BUFFER_SIZE)
{
receive_from_device_hack(pad(param)->xmit_fifo_pop());
}
}
else
{
}
}
void iop_sio2_device::transmit_to_device_hack(uint8_t data)
{
if (m_target_device == 0)
{
select_device_hack(data);
}
//bool cmd_complete = false;
if (m_cmd_size)
{
m_cmd_size--;
if (!m_cmd_size)
{
//cmd_complete = true;
}
}
switch (m_target_device)
{
case ps2_pad_device::SIO_DEVICE_ID:
m_unknown_0x6c = 0x0001d100; // TODO: As above
receive_from_device_hack(0);
/*m_unknown_0x6c = 0x00001100; // TODO: What do these bits mean and why does the code expect them?
pad(m_curr_port)->recv_fifo_push(data);
if (!m_cmd_size)
{
m_response_timer->adjust(attotime::from_ticks(8*pad(m_curr_port)->xmit_fifo_depth(), 250'000), m_curr_port);
m_curr_port++;
}*/
break;
case ps2_mc_device::SIO_DEVICE_ID:
/*if (m_cmd_size || cmd_complete)
{
logerror("Pushing %02x to memory card, m_cmd_size is %d\n", data, m_cmd_size);
m_mc0->recv_fifo_push(data);
}
if (cmd_complete)
{
logerror("Command is complete, processing fifos\n");
m_unknown_0x6c = 0x00001100;
m_mc0->process_fifos();
while (m_mc0->xmit_fifo_depth() && m_end_byte < BUFFER_SIZE)
{
logerror("xmit fifo is %d, end byte is %d\n", m_mc0->xmit_fifo_depth(), m_end_byte);
receive_from_device_hack(m_mc0->xmit_fifo_pop());
}
m_unknown_0x74 = m_cmd_length;
}*/
m_unknown_0x6c = 0x0001d100; // TODO: As above
receive_from_device_hack(0);
break;
default:
m_unknown_0x6c = 0x0001d100; // TODO: As above
receive_from_device_hack(0);
logerror("%s: Unknown device selected, can't write data %02x\n", machine().describe_context(), data);
break;
}
}
void iop_sio2_device::select_device_hack(uint8_t data)
{
switch (data)
{
case ps2_pad_device::SIO_DEVICE_ID:
m_target_device = data;
// TODO: Select transmit frequency, etc.
//logerror("%s: Selecting transmit device: Pad\n", machine().describe_context());
break;
case ps2_mc_device::SIO_DEVICE_ID:
m_target_device = data;
//logerror("%s: Selecting transmit device: Memcard\n", machine().describe_context());
break;
default:
m_target_device = 0xff;
//logerror("%s: Selecting transmit device: Unknown\n", machine().describe_context());
break;
}
}
void iop_sio2_device::receive_from_device_hack(uint8_t data)
{
if (m_end_byte == BUFFER_SIZE)
{
return;
}
m_buffer[m_end_byte++] = data;
//logerror("%s: Receiving byte from device (new top: %d)\n", machine().describe_context(), m_end_byte);
}