mirror of
https://github.com/holub/mame
synced 2025-04-21 07:52:35 +03:00
ps2sony: Checkpoint, nw
This commit is contained in:
parent
d5d82fa30a
commit
03927c3426
@ -3064,14 +3064,20 @@ files {
|
||||
MAME_DIR .. "src/mame/machine/ps2intc.h",
|
||||
MAME_DIR .. "src/mame/machine/ps2sif.cpp",
|
||||
MAME_DIR .. "src/mame/machine/ps2sif.h",
|
||||
MAME_DIR .. "src/mame/machine/iopcdvd.cpp",
|
||||
MAME_DIR .. "src/mame/machine/iopcdvd.h",
|
||||
MAME_DIR .. "src/mame/machine/iopdma.cpp",
|
||||
MAME_DIR .. "src/mame/machine/iopdma.h",
|
||||
MAME_DIR .. "src/mame/machine/iopintc.cpp",
|
||||
MAME_DIR .. "src/mame/machine/iopintc.h",
|
||||
MAME_DIR .. "src/mame/audio/iopspu.cpp",
|
||||
MAME_DIR .. "src/mame/audio/iopspu.h",
|
||||
MAME_DIR .. "src/mame/machine/iopsio2.cpp",
|
||||
MAME_DIR .. "src/mame/machine/iopsio2.h",
|
||||
MAME_DIR .. "src/mame/machine/iopspu.cpp",
|
||||
MAME_DIR .. "src/mame/machine/iopspu.h",
|
||||
MAME_DIR .. "src/mame/machine/ioptimer.cpp",
|
||||
MAME_DIR .. "src/mame/machine/ioptimer.h",
|
||||
MAME_DIR .. "src/mame/audio/iopspu.cpp",
|
||||
MAME_DIR .. "src/mame/audio/iopspu.h",
|
||||
}
|
||||
|
||||
createMESSProjects(_target, _subtarget, "sord")
|
||||
|
@ -3443,20 +3443,16 @@ void r5900le_device::handle_idt(uint32_t op)
|
||||
case 0x10: /* MFHI1 */
|
||||
if (rd)
|
||||
m_core->r[rd] = m_core->rh[REG_HI];
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x11: /* MTHI1 */
|
||||
m_core->rh[REG_HI] = m_core->r[rs];
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x12: /* MFLO1 */
|
||||
if (rd)
|
||||
m_core->r[rd] = m_core->rh[REG_LO];
|
||||
m_core->icount--;
|
||||
if (rd)
|
||||
m_core->r[rd] = m_core->rh[REG_LO];
|
||||
break;
|
||||
case 0x13: /* MTLO1 */
|
||||
m_core->rh[REG_LO] = m_core->r[rs];
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x18: /* MULT1 */
|
||||
{
|
||||
@ -3565,7 +3561,24 @@ void r5900le_device::handle_mmi0(uint32_t op)
|
||||
printf("Unsupported instruction: PADDB @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
break;
|
||||
case 0x09: /* PSUBB */
|
||||
printf("Unsupported instruction: PSUBB @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
if (rd)
|
||||
{
|
||||
const uint64_t rsval[2] = { m_core->rh[rs], m_core->r[rs] };
|
||||
const uint64_t rtval[2] = { m_core->rh[rt], m_core->r[rt] };
|
||||
uint64_t rdval[2] = { 0, 0 };
|
||||
for (int word_idx = 0; word_idx < 2; word_idx++)
|
||||
{
|
||||
for (int byte_idx = 0; byte_idx < 64; byte_idx += 8)
|
||||
{
|
||||
const uint8_t rsbyte = (uint8_t)(rsval[word_idx] >> byte_idx);
|
||||
const uint8_t rtbyte = (uint8_t)(rtval[word_idx] >> byte_idx);
|
||||
const uint8_t result = rsbyte - rtbyte;
|
||||
rdval[word_idx] |= (uint64_t)result << byte_idx;
|
||||
}
|
||||
}
|
||||
m_core->rh[rd] = rdval[0];
|
||||
m_core->r[rd] = rdval[1];
|
||||
}
|
||||
break;
|
||||
case 0x0a: /* PCGTB */
|
||||
printf("Unsupported instruction: PCGTB @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
@ -3674,7 +3687,6 @@ void r5900le_device::handle_mmi1(uint32_t op)
|
||||
}
|
||||
m_core->r[rd] = rdval[0] | (rdval[1] << 32);
|
||||
m_core->rh[rd] = rdval[2] | (rdval[3] << 32);
|
||||
m_core->icount -= 1;
|
||||
break;
|
||||
}
|
||||
case 0x11: /* PSUBUW */
|
||||
@ -3736,7 +3748,6 @@ void r5900le_device::handle_mmi2(uint32_t op)
|
||||
m_core->r[rd] = m_core->r[REG_HI];
|
||||
m_core->rh[rd] = m_core->rh[REG_HI];
|
||||
}
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x09: /* PMFLO */
|
||||
if (rd)
|
||||
@ -3744,7 +3755,6 @@ void r5900le_device::handle_mmi2(uint32_t op)
|
||||
m_core->r[rd] = m_core->r[REG_LO];
|
||||
m_core->rh[rd] = m_core->rh[REG_LO];
|
||||
}
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x0a: /* PINTH */
|
||||
printf("Unsupported instruction: PINTH @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
@ -3761,7 +3771,6 @@ void r5900le_device::handle_mmi2(uint32_t op)
|
||||
m_core->rh[rd] = m_core->r[rs];
|
||||
m_core->r[rd] = m_core->r[rt];
|
||||
}
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x10: /* PMADDH */
|
||||
printf("Unsupported instruction: PMADDH @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
@ -3770,10 +3779,18 @@ void r5900le_device::handle_mmi2(uint32_t op)
|
||||
printf("Unsupported instruction: PHMADH @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
break;
|
||||
case 0x12: /* PAND */
|
||||
printf("Unsupported instruction: PAND @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
if (rd)
|
||||
{
|
||||
m_core->rh[rd] = m_core->rh[rs] & m_core->rh[rt];
|
||||
m_core->r[rd] = m_core->r[rs] & m_core->r[rt];
|
||||
}
|
||||
break;
|
||||
case 0x13: /* PXOR */
|
||||
printf("Unsupported instruction: PXOR @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
if (rd)
|
||||
{
|
||||
m_core->rh[rd] = m_core->rh[rs] ^ m_core->rh[rt];
|
||||
m_core->r[rd] = m_core->r[rs] ^ m_core->r[rt];
|
||||
}
|
||||
break;
|
||||
case 0x14: /* PMSUBH */
|
||||
printf("Unsupported instruction: PMSUBH @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
@ -3840,7 +3857,6 @@ void r5900le_device::handle_mmi3(uint32_t op)
|
||||
m_core->rh[rd] = m_core->rh[rs];
|
||||
m_core->r[rd] = m_core->rh[rt];
|
||||
}
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x12: /* POR */
|
||||
if (rd)
|
||||
@ -3848,16 +3864,26 @@ void r5900le_device::handle_mmi3(uint32_t op)
|
||||
m_core->rh[rd] = m_core->rh[rs] | m_core->rh[rt];
|
||||
m_core->r[rd] = m_core->r[rs] | m_core->r[rt];
|
||||
}
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x13: /* PNOR */
|
||||
printf("Unsupported instruction: PNOR @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
if (rd)
|
||||
{
|
||||
m_core->rh[rd] = ~(m_core->rh[rs] | m_core->rh[rt]);
|
||||
m_core->r[rd] = ~(m_core->r[rs] | m_core->r[rt]);
|
||||
}
|
||||
break;
|
||||
case 0x1a: /* PEXCH */
|
||||
printf("Unsupported instruction: PEXCH @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
break;
|
||||
case 0x1b: /* PCPYH */
|
||||
printf("Unsupported instruction: PCPYH @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
if (rd)
|
||||
{
|
||||
const uint16_t msh = (uint16_t)m_core->rh[rt];
|
||||
const uint16_t lsh = (uint16_t)m_core->r[rt];
|
||||
m_core->rh[rd] = msh * 0x0001000100010001ULL;
|
||||
m_core->r[rd] = lsh * 0x0001000100010001ULL;
|
||||
}
|
||||
m_core->icount--;
|
||||
break;
|
||||
case 0x1e: /* PEXCW */
|
||||
printf("Unsupported instruction: PEXCW @%08x\n", m_core->pc - 4); fflush(stdout); fatalerror("Unsupported parallel instruction\n");
|
||||
|
@ -15,80 +15,152 @@ DEFINE_DEVICE_TYPE(SONYIOP_SPU, iop_spu_device, "iopspu", "Playstation 2 IOP SPU
|
||||
|
||||
iop_spu_device::iop_spu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, SONYIOP_SPU, tag, owner, clock)
|
||||
, device_sound_interface(mconfig, *this)
|
||||
, m_iop(*this, finder_base::DUMMY_TAG)
|
||||
, m_intc(*this, finder_base::DUMMY_TAG)
|
||||
{
|
||||
}
|
||||
|
||||
void iop_spu_device::device_start()
|
||||
{
|
||||
m_ram = std::make_unique<uint16_t[]>(2 * 1024 * 1024); // ?
|
||||
|
||||
if (!m_core[0].m_autodma_done_timer_hack)
|
||||
m_core[0].m_autodma_done_timer_hack = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(iop_spu_device::autodma_done_timer_hack), this), 0);
|
||||
|
||||
if (!m_core[1].m_autodma_done_timer_hack)
|
||||
m_core[1].m_autodma_done_timer_hack = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(iop_spu_device::autodma_done_timer_hack), this), (void*)1);
|
||||
|
||||
save_item(NAME(m_core[0].m_status));
|
||||
save_item(NAME(m_core[0].m_start_port_addr));
|
||||
save_item(NAME(m_core[0].m_curr_port_addr));
|
||||
save_item(NAME(m_core[0].m_unknown_0x19a));
|
||||
save_item(NAME(m_core[0].m_autodma_ctrl));
|
||||
save_item(NAME(m_core[1].m_status));
|
||||
save_item(NAME(m_core[1].m_start_port_addr));
|
||||
save_item(NAME(m_core[1].m_curr_port_addr));
|
||||
save_item(NAME(m_core[1].m_unknown_0x19a));
|
||||
save_item(NAME(m_core[1].m_autodma_ctrl));
|
||||
}
|
||||
|
||||
void iop_spu_device::device_reset()
|
||||
{
|
||||
m_status = 0;
|
||||
m_start_port_addr = 0;
|
||||
m_curr_port_addr = 0;
|
||||
m_unknown_0x19a = 0;
|
||||
m_core[0].m_status = 0;
|
||||
m_core[0].m_start_port_addr = 0;
|
||||
m_core[0].m_curr_port_addr = 0;
|
||||
m_core[0].m_unknown_0x19a = 0;
|
||||
m_core[0].m_autodma_ctrl = 0;
|
||||
m_core[1].m_status = 0;
|
||||
m_core[1].m_start_port_addr = 0;
|
||||
m_core[1].m_curr_port_addr = 0;
|
||||
m_core[1].m_unknown_0x19a = 0;
|
||||
m_core[1].m_autodma_ctrl = 0;
|
||||
}
|
||||
|
||||
void iop_spu_device::dma_write(uint32_t data)
|
||||
void iop_spu_device::dma_write(int bank, uint32_t data)
|
||||
{
|
||||
//m_ram[m_curr_port_addr] = (uint16_t)(data >> 16);
|
||||
m_curr_port_addr++;
|
||||
//m_ram[m_curr_port_addr] = (uint16_t)data;
|
||||
m_curr_port_addr++;
|
||||
iop_spu_core_t &core = m_core[bank];
|
||||
//m_ram[core.m_curr_port_addr] = (uint16_t)(data >> 16);
|
||||
core.m_curr_port_addr++;
|
||||
//m_ram[core.m_curr_port_addr] = (uint16_t)data;
|
||||
core.m_curr_port_addr++;
|
||||
|
||||
m_status &= ~STATUS_DMA_DONE;
|
||||
m_status |= STATUS_DMA_ACTIVE;
|
||||
if (core.m_curr_port_addr >= 0xfffff)
|
||||
core.m_curr_port_addr = 0x2800 >> 1;
|
||||
|
||||
core.m_status &= ~STATUS_DMA_DONE;
|
||||
core.m_status |= STATUS_DMA_ACTIVE;
|
||||
|
||||
if ((core.m_autodma_ctrl & (1 << bank)) && core.m_autodma_done_timer_hack->expire().is_never())
|
||||
{
|
||||
core.m_autodma_done_timer_hack->adjust(attotime::from_ticks(256, 48000), bank); // 256 halfwords * assumed 48kHz sampling rate
|
||||
}
|
||||
}
|
||||
|
||||
void iop_spu_device::dma_done()
|
||||
void iop_spu_device::dma_done(int bank)
|
||||
{
|
||||
m_status |= STATUS_DMA_DONE;
|
||||
m_status &= ~STATUS_DMA_ACTIVE;
|
||||
iop_spu_core_t &core = m_core[bank];
|
||||
core.m_status |= STATUS_DMA_DONE;
|
||||
core.m_status &= ~STATUS_DMA_ACTIVE;
|
||||
}
|
||||
|
||||
uint16_t iop_spu_device::port_read()
|
||||
void iop_spu_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
|
||||
{
|
||||
uint16_t ret = m_ram[m_curr_port_addr];
|
||||
m_curr_port_addr++;
|
||||
// TODO
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(iop_spu_device::autodma_done_timer_hack)
|
||||
{
|
||||
m_core[param].m_autodma_done_timer_hack->adjust(attotime::never);
|
||||
m_intc->raise_interrupt(iop_intc_device::INT_SPU);
|
||||
}
|
||||
|
||||
uint16_t iop_spu_device::port_read(int bank)
|
||||
{
|
||||
iop_spu_core_t &core = m_core[bank];
|
||||
const uint16_t ret = m_ram[core.m_curr_port_addr];
|
||||
core.m_curr_port_addr++;
|
||||
if (core.m_curr_port_addr >= 0xfffff)
|
||||
core.m_curr_port_addr = 0x2800 >> 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iop_spu_device::port_write(uint16_t data)
|
||||
void iop_spu_device::port_write(int bank, uint16_t data)
|
||||
{
|
||||
m_ram[m_curr_port_addr] = data;
|
||||
m_curr_port_addr++;
|
||||
iop_spu_core_t &core = m_core[bank];
|
||||
m_ram[core.m_curr_port_addr] = data;
|
||||
core.m_curr_port_addr++;
|
||||
if (core.m_curr_port_addr >= 0xfffff)
|
||||
core.m_curr_port_addr = 0x2800 >> 1;
|
||||
}
|
||||
|
||||
READ16_MEMBER(iop_spu_device::read)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
return reg_read(BIT(offset, 9), (offset << 1) & 0x3ff, mem_mask);
|
||||
}
|
||||
|
||||
uint16_t iop_spu_device::reg_read(int bank, uint32_t offset, uint16_t mem_mask)
|
||||
{
|
||||
iop_spu_core_t &core = m_core[bank];
|
||||
uint16_t ret = 0;
|
||||
|
||||
if (offset < 0x180)
|
||||
{
|
||||
const uint32_t reg = offset & 0xf;
|
||||
ret = core.m_voices[offset >> 4].read(reg);
|
||||
logerror("%s %d: Voice read: Unknown offset %d (%04x & %04x)\n", machine().describe_context(), bank, reg, ret, mem_mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (offset)
|
||||
{
|
||||
case 0x19a/2:
|
||||
ret = m_unknown_0x19a;
|
||||
logerror("%s: read: Unknown 0x19a (%04x & %04x)\n", machine().describe_context(), ret, mem_mask);
|
||||
case 0x19a:
|
||||
ret = core.m_unknown_0x19a;
|
||||
logerror("%s %d: read: Unknown 0x19a (%04x & %04x)\n", machine().describe_context(), bank, ret, mem_mask);
|
||||
break;
|
||||
|
||||
case 0x1a8/2:
|
||||
ret = (uint16_t)(m_start_port_addr >> 16);
|
||||
logerror("%s: read: PORT_ADDR_HI: %04x & %04x\n", machine().describe_context(), ret, mem_mask);
|
||||
case 0x1a8:
|
||||
ret = (uint16_t)(core.m_start_port_addr >> 16);
|
||||
logerror("%s %d: read: PORT_ADDR_HI: %04x & %04x\n", machine().describe_context(), bank, ret, mem_mask);
|
||||
break;
|
||||
|
||||
case 0x1aa/2:
|
||||
ret = (uint16_t)m_start_port_addr;
|
||||
logerror("%s: read: PORT_ADDR_LO: %04x & %04x\n", machine().describe_context(), ret, mem_mask);
|
||||
case 0x1aa:
|
||||
ret = (uint16_t)core.m_start_port_addr;
|
||||
logerror("%s %d: read: PORT_ADDR_LO: %04x & %04x\n", machine().describe_context(), bank, ret, mem_mask);
|
||||
break;
|
||||
|
||||
case 0x344/2:
|
||||
ret = m_status;
|
||||
logerror("%s: read: STATUS: %04x & %04x\n", machine().describe_context(), ret, mem_mask);
|
||||
case 0x1b0:
|
||||
ret = core.m_autodma_ctrl;
|
||||
logerror("%s %d: read: Auto DMA control: %04x & %04x\n", machine().describe_context(), bank, ret, mem_mask);
|
||||
break;
|
||||
|
||||
case 0x344:
|
||||
ret = core.m_status;
|
||||
logerror("%s %d: read: STATUS: %04x & %04x\n", machine().describe_context(), bank, ret, mem_mask);
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: read: Unknown %08x (%04x)\n", machine().describe_context(), 0x1f900000 + (offset << 1), mem_mask);
|
||||
logerror("%s %d: read: Unknown %08x (%04x)\n", machine().describe_context(), bank, 0x1f900000 + (offset << 1) + (bank << 10), mem_mask);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
@ -96,29 +168,65 @@ READ16_MEMBER(iop_spu_device::read)
|
||||
|
||||
WRITE16_MEMBER(iop_spu_device::write)
|
||||
{
|
||||
reg_write(BIT(offset, 9), (offset << 1) & 0x3ff, data, mem_mask);
|
||||
}
|
||||
|
||||
void iop_spu_device::reg_write(int bank, uint32_t offset, uint16_t data, uint16_t mem_mask)
|
||||
{
|
||||
iop_spu_core_t &core = m_core[bank];
|
||||
|
||||
if (offset < 0x180)
|
||||
{
|
||||
const uint32_t reg = offset & 0xf;
|
||||
logerror("%s %d: Voice write: Unknown offset %d = %04x & %04x\n", machine().describe_context(), bank, reg, data, mem_mask);
|
||||
core.m_voices[offset >> 4].write(reg, data);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (offset)
|
||||
{
|
||||
case 0x19a/2:
|
||||
logerror("%s: write: Unknown 0x19a = %04x & %04x\n", machine().describe_context(), data, mem_mask);
|
||||
m_unknown_0x19a = data;
|
||||
case 0x19a:
|
||||
logerror("%s %d: write: Unknown 0x19a = %04x & %04x\n", machine().describe_context(), bank, data, mem_mask);
|
||||
if (BIT(data, 15)) // Reset?
|
||||
{
|
||||
data &= 0x7fff;
|
||||
core.m_status = 0;
|
||||
core.m_autodma_ctrl = 0;
|
||||
}
|
||||
core.m_unknown_0x19a = data;
|
||||
break;
|
||||
|
||||
case 0x1a8/2:
|
||||
logerror("%s: write: PORT_ADDR_HI: %04x & %04x\n", machine().describe_context(), data, mem_mask);
|
||||
m_start_port_addr &= ~0x000f0000;
|
||||
m_start_port_addr |= ((uint32_t)data << 16) & 0x000f0000;
|
||||
m_curr_port_addr = m_start_port_addr;
|
||||
case 0x1a8:
|
||||
logerror("%s %d: write: PORT_ADDR_HI: %04x & %04x\n", machine().describe_context(), bank, data, mem_mask);
|
||||
core.m_start_port_addr &= ~0x000f0000;
|
||||
core.m_start_port_addr |= ((uint32_t)data << 16) & 0x000f0000;
|
||||
core.m_curr_port_addr = core.m_start_port_addr;
|
||||
break;
|
||||
|
||||
case 0x1aa/2:
|
||||
logerror("%s: write: PORT_ADDR_LO: %04x & %04x\n", machine().describe_context(), data, mem_mask);
|
||||
m_start_port_addr &= ~0x0000ffff;
|
||||
m_start_port_addr |= data;
|
||||
m_curr_port_addr = m_start_port_addr;
|
||||
case 0x1aa:
|
||||
logerror("%s %d: write: PORT_ADDR_LO: %04x & %04x\n", machine().describe_context(), bank, data, mem_mask);
|
||||
core.m_start_port_addr &= ~0x0000ffff;
|
||||
core.m_start_port_addr |= data;
|
||||
core.m_curr_port_addr = core.m_start_port_addr;
|
||||
break;
|
||||
|
||||
case 0x1b0:
|
||||
logerror("%s %d: write: Auto DMA control: %04x & %04x\n", machine().describe_context(), bank, data, mem_mask);
|
||||
core.m_autodma_ctrl = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: write: Unknown %08x = %04x & %04x\n", machine().describe_context(), 0x1f900000 + (offset << 1), data, mem_mask);
|
||||
logerror("%s %d: write: Unknown %08x = %04x & %04x\n", machine().describe_context(), bank, 0x1f900000 + (offset << 1) + (bank << 10), data, mem_mask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void iop_spu_device::voice_t::write(uint32_t offset, uint16_t data)
|
||||
{
|
||||
m_unknown[(offset >> 1) & 7] = data;
|
||||
}
|
||||
|
||||
uint16_t iop_spu_device::voice_t::read(uint32_t offset)
|
||||
{
|
||||
return m_unknown[(offset >> 1) & 7];
|
||||
}
|
||||
|
@ -16,15 +16,17 @@
|
||||
|
||||
#include "emu.h"
|
||||
#include "cpu/mips/r3000.h"
|
||||
#include "machine/iopintc.h"
|
||||
|
||||
class iop_spu_device : public device_t
|
||||
class iop_spu_device : public device_t, public device_sound_interface
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
iop_spu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&iop_tag)
|
||||
template <typename T, typename U>
|
||||
iop_spu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&iop_tag, U &&intc_tag)
|
||||
: iop_spu_device(mconfig, tag, owner, clock)
|
||||
{
|
||||
m_iop.set_tag(std::forward<T>(iop_tag));
|
||||
m_intc.set_tag(std::forward<U>(intc_tag));
|
||||
}
|
||||
|
||||
iop_spu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
@ -32,29 +34,66 @@ public:
|
||||
DECLARE_READ16_MEMBER(read);
|
||||
DECLARE_WRITE16_MEMBER(write);
|
||||
|
||||
uint16_t port_read();
|
||||
void port_write(uint16_t data);
|
||||
uint16_t reg_read(int bank, uint32_t offset, uint16_t mem_mask);
|
||||
void reg_write(int bank, uint32_t offset, uint16_t data, uint16_t mem_mask);
|
||||
|
||||
void dma_write(uint32_t data);
|
||||
void dma_done();
|
||||
uint16_t port_read(int bank);
|
||||
void port_write(int bank, uint16_t data);
|
||||
|
||||
void dma_write(int bank, uint32_t data);
|
||||
void dma_done(int bank);
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// sound stream update overrides
|
||||
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
|
||||
|
||||
// HACK: This timer is currently used to trigger an interrupt after the auto-DMA-transferred buffer would have been
|
||||
// mixed and played back, as the PS2 BIOS pulls a null return address and crashes if we trigger the auto-DMA-complete
|
||||
// interrupt at the same time as the DMA-complete interrupt.
|
||||
TIMER_CALLBACK_MEMBER(autodma_done_timer_hack);
|
||||
|
||||
enum
|
||||
{
|
||||
STATUS_DMA_DONE = (1 << 7),
|
||||
STATUS_DMA_ACTIVE = (1 << 10)
|
||||
};
|
||||
|
||||
class voice_t
|
||||
{
|
||||
public:
|
||||
voice_t() {}
|
||||
|
||||
void write(uint32_t offset, uint16_t data);
|
||||
uint16_t read(uint32_t offset);
|
||||
|
||||
protected:
|
||||
uint16_t m_unknown[8];
|
||||
};
|
||||
|
||||
required_device<iop_device> m_iop;
|
||||
required_device<iop_intc_device> m_intc;
|
||||
std::unique_ptr<uint16_t[]> m_ram;
|
||||
|
||||
uint32_t m_status;
|
||||
uint32_t m_start_port_addr;
|
||||
uint32_t m_curr_port_addr;
|
||||
uint32_t m_unknown_0x19a;
|
||||
struct iop_spu_core_t
|
||||
{
|
||||
voice_t m_voices[24];
|
||||
|
||||
uint32_t m_start_port_addr;
|
||||
uint32_t m_curr_port_addr;
|
||||
|
||||
uint16_t m_status;
|
||||
uint16_t m_unknown_0x19a;
|
||||
uint16_t m_autodma_ctrl;
|
||||
|
||||
emu_timer *m_autodma_done_timer_hack;
|
||||
};
|
||||
|
||||
iop_spu_core_t m_core[2];
|
||||
|
||||
sound_stream *m_stream;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SONYIOP_SPU, iop_spu_device)
|
||||
|
@ -155,17 +155,25 @@ iLinkSGUID=0x--------
|
||||
|
||||
(*) values are not detected
|
||||
************************************************************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
#include "audio/iopspu.h"
|
||||
|
||||
#include "cpu/mips/mips3.h"
|
||||
#include "cpu/mips/r3000.h"
|
||||
#include "machine/ioptimer.h"
|
||||
|
||||
#include "machine/iopcdvd.h"
|
||||
#include "machine/iopdma.h"
|
||||
#include "machine/iopintc.h"
|
||||
#include "machine/ps2timer.h"
|
||||
#include "machine/iopsio2.h"
|
||||
#include "machine/ioptimer.h"
|
||||
|
||||
#include "machine/ps2dma.h"
|
||||
#include "machine/ps2intc.h"
|
||||
#include "machine/ps2sif.h"
|
||||
#include "machine/ps2timer.h"
|
||||
|
||||
#include "emupal.h"
|
||||
#include "screen.h"
|
||||
|
||||
@ -183,7 +191,9 @@ public:
|
||||
, m_iop_timer(*this, "iop_timer")
|
||||
, m_iop_dma(*this, "iop_dma")
|
||||
, m_iop_intc(*this, "iop_intc")
|
||||
, m_iop_spu(*this, "iop_spu%u", 1U)
|
||||
, m_iop_spu(*this, "iop_spu")
|
||||
, m_iop_cdvd(*this, "iop_cdvd")
|
||||
, m_iop_sio2(*this, "iop_sio2")
|
||||
, m_screen(*this, "screen")
|
||||
, m_ram(*this, "ram")
|
||||
, m_iop_ram(*this, "iop_ram")
|
||||
@ -231,12 +241,6 @@ protected:
|
||||
DECLARE_WRITE64_MEMBER(ee_iop_ram_w);
|
||||
DECLARE_READ64_MEMBER(ee_iop_ram_r);
|
||||
DECLARE_WRITE32_MEMBER(iop_debug_w);
|
||||
DECLARE_READ32_MEMBER(unk_iop_r);
|
||||
DECLARE_WRITE32_MEMBER(unk_iop_w);
|
||||
DECLARE_WRITE16_MEMBER(iop_spu_w);
|
||||
DECLARE_READ16_MEMBER(iop_spu_r);
|
||||
DECLARE_WRITE16_MEMBER(iop_spu2_w);
|
||||
DECLARE_READ16_MEMBER(iop_spu2_r);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(iop_timer_irq);
|
||||
|
||||
@ -252,7 +256,9 @@ protected:
|
||||
required_device<iop_timer_device> m_iop_timer;
|
||||
required_device<iop_dma_device> m_iop_dma;
|
||||
required_device<iop_intc_device> m_iop_intc;
|
||||
required_device_array<iop_spu_device, 2> m_iop_spu;
|
||||
required_device<iop_spu_device> m_iop_spu;
|
||||
required_device<iop_cdvd_device> m_iop_cdvd;
|
||||
required_device<iop_sio2_device> m_iop_sio2;
|
||||
required_device<screen_device> m_screen;
|
||||
required_shared_ptr<uint64_t> m_ram;
|
||||
required_shared_ptr<uint32_t> m_iop_ram;
|
||||
@ -279,12 +285,6 @@ protected:
|
||||
uint64_t m_ipu_out_fifo[0x1000];
|
||||
uint64_t m_ipu_out_fifo_index;
|
||||
|
||||
uint32_t m_sif_ms_mailbox;
|
||||
uint32_t m_sif_sm_mailbox;
|
||||
uint32_t m_sif_ms_flag;
|
||||
uint32_t m_sif_sm_flag;
|
||||
uint32_t m_sif_ctrl;
|
||||
|
||||
emu_timer *m_vblank_timer;
|
||||
};
|
||||
|
||||
@ -543,50 +543,6 @@ WRITE32_MEMBER(ps2sony_state::iop_debug_w)
|
||||
//printf("%08x ", data);
|
||||
}
|
||||
|
||||
READ32_MEMBER(ps2sony_state::unk_iop_r)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
switch (offset)
|
||||
{
|
||||
case 1:
|
||||
if (mem_mask & 0xff00)
|
||||
ret |= 0x4000;
|
||||
logerror("%s; unk_iop_r: Unknown read: %08x = %08x & %08x\n", machine().describe_context(), 0x1f402000 + (offset << 2), ret, mem_mask);
|
||||
break;
|
||||
default:
|
||||
logerror("%s; unk_iop_r: Unknown read: %08x & %08x\n", machine().describe_context(), 0x1f402000 + (offset << 2), mem_mask);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
WRITE32_MEMBER(ps2sony_state::unk_iop_w)
|
||||
{
|
||||
logerror("%s; unk_iop_w: Unknown write: %08x = %08x & %08x\n", machine().describe_context(), 0x1f402000 + (offset << 2), data, mem_mask);
|
||||
}
|
||||
|
||||
WRITE16_MEMBER(ps2sony_state::iop_spu2_w)
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
default:
|
||||
logerror("%s: iop_spu2_w: Unknown %08x = %04x & %04x\n", 0x1f900000 + (offset << 1), data, mem_mask);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
READ16_MEMBER(ps2sony_state::iop_spu2_r)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
switch (offset)
|
||||
{
|
||||
default:
|
||||
logerror("%s: iop_spu2_r: Unknown %08x (%04x)\n", 0x1f900000 + (offset << 1), mem_mask);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ps2sony_state::machine_start()
|
||||
{
|
||||
save_item(NAME(m_gs_base_regs));
|
||||
@ -630,12 +586,6 @@ void ps2sony_state::machine_reset()
|
||||
m_gs_sig_label_id = 0ULL;
|
||||
|
||||
m_vblank_timer->adjust(m_screen->time_until_pos(480), 1);
|
||||
|
||||
m_sif_ms_mailbox = 0;
|
||||
m_sif_sm_mailbox = 0;
|
||||
m_sif_ms_flag = 0;
|
||||
m_sif_sm_flag = 0;
|
||||
m_sif_ctrl = 0;
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(ps2sony_state::vblank)
|
||||
@ -901,7 +851,7 @@ void ps2sony_state::iop_map(address_map &map)
|
||||
map(0x00000000, 0x001fffff).ram().share(m_iop_ram);
|
||||
map(0x1d000000, 0x1d00004f).rw(m_sif, FUNC(ps2_sif_device::iop_r), FUNC(ps2_sif_device::iop_w));
|
||||
map(0x1e000000, 0x1e003fff).nopr();
|
||||
map(0x1f402000, 0x1f40200f).rw(FUNC(ps2sony_state::unk_iop_r), FUNC(ps2sony_state::unk_iop_w));
|
||||
map(0x1f402000, 0x1f40201f).rw(m_iop_cdvd, FUNC(iop_cdvd_device::read), FUNC(iop_cdvd_device::write));
|
||||
map(0x1f801070, 0x1f80107b).rw(m_iop_intc, FUNC(iop_intc_device::read), FUNC(iop_intc_device::write));
|
||||
map(0x1f801080, 0x1f8010f7).rw(m_iop_dma, FUNC(iop_dma_device::bank0_r), FUNC(iop_dma_device::bank0_w));
|
||||
map(0x1f801450, 0x1f801453).noprw();
|
||||
@ -909,8 +859,8 @@ void ps2sony_state::iop_map(address_map &map)
|
||||
map(0x1f801500, 0x1f801577).rw(m_iop_dma, FUNC(iop_dma_device::bank1_r), FUNC(iop_dma_device::bank1_w));
|
||||
map(0x1f801578, 0x1f80157b).noprw();
|
||||
map(0x1f802070, 0x1f802073).w(FUNC(ps2sony_state::iop_debug_w)).nopr();
|
||||
map(0x1f900000, 0x1f9003ff).rw(m_iop_spu[0], FUNC(iop_spu_device::read), FUNC(iop_spu_device::write));
|
||||
map(0x1f900400, 0x1f9007ff).rw(m_iop_spu[1], FUNC(iop_spu_device::read), FUNC(iop_spu_device::write));
|
||||
map(0x1f808200, 0x1f8082ff).rw(m_iop_sio2, FUNC(iop_sio2_device::read), FUNC(iop_sio2_device::write));
|
||||
map(0x1f900000, 0x1f9007ff).rw(m_iop_spu, FUNC(iop_spu_device::read), FUNC(iop_spu_device::write));
|
||||
map(0x1fc00000, 0x1fffffff).rom().region("bios", 0);
|
||||
map(0x1ffe0130, 0x1ffe0133).nopw();
|
||||
}
|
||||
@ -941,12 +891,13 @@ MACHINE_CONFIG_START(ps2sony_state::ps2sony)
|
||||
MCFG_QUANTUM_PERFECT_CPU("iop")
|
||||
|
||||
MCFG_DEVICE_ADD(m_iop_intc, SONYIOP_INTC, m_iop)
|
||||
MCFG_DEVICE_ADD(m_iop_sio2, SONYIOP_SIO2, m_iop_intc)
|
||||
MCFG_DEVICE_ADD(m_iop_cdvd, SONYIOP_CDVD, m_iop_intc)
|
||||
MCFG_DEVICE_ADD(m_iop_timer, SONYIOP_TIMER, XTAL(67'737'600)/2)
|
||||
MCFG_IOP_TIMER_IRQ_CALLBACK(WRITELINE(*this, ps2sony_state, iop_timer_irq))
|
||||
MCFG_DEVICE_ADD(m_iop_spu[0], SONYIOP_SPU, XTAL(67'737'600)/2, m_iop)
|
||||
MCFG_DEVICE_ADD(m_iop_spu[1], SONYIOP_SPU, XTAL(67'737'600)/2, m_iop)
|
||||
MCFG_DEVICE_ADD(m_iop_spu, SONYIOP_SPU, XTAL(67'737'600)/2, m_iop, m_iop_intc)
|
||||
|
||||
MCFG_DEVICE_ADD(m_iop_dma, SONYIOP_DMA, XTAL(67'737'600)/2, m_iop_intc, m_iop_ram, m_sif, m_iop_spu[0], m_iop_spu[1])
|
||||
MCFG_DEVICE_ADD(m_iop_dma, SONYIOP_DMA, XTAL(67'737'600)/2, m_iop_intc, m_iop_ram, m_sif, m_iop_spu, m_iop_sio2)
|
||||
|
||||
/* video hardware */
|
||||
MCFG_SCREEN_ADD("screen", RASTER)
|
||||
|
122
src/mame/machine/iopcdvd.cpp
Normal file
122
src/mame/machine/iopcdvd.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Ryan Holtz
|
||||
/******************************************************************************
|
||||
*
|
||||
* Sony Playstation 2 disc controller device skeleton
|
||||
*
|
||||
* To Do:
|
||||
* Everything
|
||||
*
|
||||
*/
|
||||
|
||||
#include "iopcdvd.h"
|
||||
|
||||
/*static*/ const size_t iop_cdvd_device::BUFFER_SIZE = 2048; // Total guess
|
||||
|
||||
DEFINE_DEVICE_TYPE(SONYIOP_CDVD, iop_cdvd_device, "iopcdvd", "Playstation 2 disc controller")
|
||||
|
||||
iop_cdvd_device::iop_cdvd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, SONYIOP_CDVD, tag, owner, clock)
|
||||
, m_intc(*this, finder_base::DUMMY_TAG)
|
||||
{
|
||||
}
|
||||
|
||||
void iop_cdvd_device::device_start()
|
||||
{
|
||||
m_out_buf = std::make_unique<uint8_t[]>(BUFFER_SIZE);
|
||||
|
||||
save_item(NAME(m_status_0x05));
|
||||
save_item(NAME(m_status_0x17));
|
||||
save_item(NAME(m_command));
|
||||
save_item(NAME(m_out_count));
|
||||
save_item(NAME(m_out_curr));
|
||||
}
|
||||
|
||||
void iop_cdvd_device::device_reset()
|
||||
{
|
||||
m_status_0x05 = CDVD_STATUS_BOOT;
|
||||
m_status_0x17 = CDVD_STATUS_IDLE;
|
||||
m_command = 0;
|
||||
memset(&m_out_buf[0], 0, BUFFER_SIZE);
|
||||
m_out_count = 0;
|
||||
m_out_curr = 0;
|
||||
}
|
||||
|
||||
READ8_MEMBER(iop_cdvd_device::read)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
switch (offset)
|
||||
{
|
||||
case 0x05:
|
||||
ret = m_status_0x05;
|
||||
logerror("%s: cdvd_r: Status 0x05? (%02x)\n", machine().describe_context(), ret);
|
||||
break;
|
||||
case 0x16:
|
||||
ret = m_command;
|
||||
logerror("%s: cdvd_r: Command? (%02x)\n", machine().describe_context(), ret);
|
||||
break;
|
||||
case 0x17:
|
||||
ret = m_status_0x17;
|
||||
logerror("%s: cdvd_r: Status 0x17? (%02x)\n", machine().describe_context(), ret);
|
||||
break;
|
||||
case 0x18:
|
||||
ret = m_out_buf[m_out_curr];
|
||||
if (m_out_curr < m_out_count)
|
||||
{
|
||||
m_out_curr++;
|
||||
if (m_out_curr == m_out_count)
|
||||
{
|
||||
m_status_0x17 |= CDVD_STATUS_IDLE;
|
||||
m_out_count = 0;
|
||||
m_out_curr = 0;
|
||||
}
|
||||
}
|
||||
logerror("%s: cdvd_r: Command Output (%02x) (%d left)\n", machine().describe_context(), ret, m_out_count - m_out_curr);
|
||||
break;
|
||||
default:
|
||||
logerror("%s: cdvd_r: Unknown read: %08x\n", machine().describe_context(), 0x1f402000 + offset);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(iop_cdvd_device::write)
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
case 0x16:
|
||||
m_command = data;
|
||||
m_status_0x17 &= ~(CDVD_STATUS_IDLE | CDVD_STATUS_BOOT);
|
||||
m_status_0x05 &= ~CDVD_STATUS_BOOT;
|
||||
if (m_out_count > 0)
|
||||
logerror("%s: cdvd_w: Command warning: Issuing command without reading previous results\n", machine().describe_context());
|
||||
switch (data)
|
||||
{
|
||||
case 0x15:
|
||||
logerror("%s: cdvd_w: Command 0x15?\n", machine().describe_context());
|
||||
m_out_buf[m_out_count++] = 1;
|
||||
m_intc->raise_interrupt(5);
|
||||
break;
|
||||
case 0x40:
|
||||
logerror("%s: cdvd_w: Command 0x40?\n", machine().describe_context());
|
||||
m_out_buf[m_out_count++] = 0;
|
||||
break;
|
||||
case 0x41:
|
||||
logerror("%s: cdvd_w: Command 0x41?\n", machine().describe_context());
|
||||
m_out_count = 0x10;
|
||||
memset(&m_out_buf[0], 0, 0x10);
|
||||
break;
|
||||
case 0x43:
|
||||
logerror("%s: cdvd_w: Command 0x43?\n", machine().describe_context());
|
||||
m_out_buf[m_out_count++] = 0;
|
||||
break;
|
||||
default:
|
||||
logerror("%s: cdvd_r: Unknown command(?) %02x\n", machine().describe_context(), data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logerror("%s: cdvd_w: Unknown write: %08x = %02x\n", machine().describe_context(), 0x1f402000 + offset, data);
|
||||
break;
|
||||
}
|
||||
}
|
60
src/mame/machine/iopcdvd.h
Normal file
60
src/mame/machine/iopcdvd.h
Normal file
@ -0,0 +1,60 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Ryan Holtz
|
||||
/******************************************************************************
|
||||
*
|
||||
* Sony Playstation 2 disc controller device skeleton
|
||||
*
|
||||
* To Do:
|
||||
* Everything
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAME_MACHINE_IOPCDVD_H
|
||||
#define MAME_MACHINE_IOPCDVD_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "emu.h"
|
||||
#include "iopintc.h"
|
||||
|
||||
class iop_cdvd_device : public device_t
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
iop_cdvd_device(const machine_config &mconfig, const char *tag, device_t *owner, T &&intc_tag)
|
||||
: iop_cdvd_device(mconfig, tag, owner, (uint32_t)0)
|
||||
{
|
||||
m_intc.set_tag(std::forward<T>(intc_tag));
|
||||
}
|
||||
|
||||
iop_cdvd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
DECLARE_READ8_MEMBER(read);
|
||||
DECLARE_WRITE8_MEMBER(write);
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
enum
|
||||
{
|
||||
CDVD_STATUS_BOOT = 0x08,
|
||||
CDVD_STATUS_IDLE = 0x40,
|
||||
|
||||
CDVD_COMMAND_0x15 = 0x15,
|
||||
};
|
||||
|
||||
required_device<iop_intc_device> m_intc;
|
||||
uint8_t m_status_0x05;
|
||||
uint8_t m_status_0x17;
|
||||
uint8_t m_command;
|
||||
std::unique_ptr<uint8_t[]> m_out_buf;
|
||||
uint8_t m_out_count;
|
||||
uint8_t m_out_curr;
|
||||
|
||||
static const size_t BUFFER_SIZE;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SONYIOP_CDVD, iop_cdvd_device)
|
||||
|
||||
#endif // MAME_MACHINE_IOPCDVD_H
|
@ -21,7 +21,7 @@ iop_dma_device::iop_dma_device(const machine_config &mconfig, const char *tag, d
|
||||
, m_ram(*this, finder_base::DUMMY_TAG)
|
||||
, m_sif(*this, finder_base::DUMMY_TAG)
|
||||
, m_spu(*this, finder_base::DUMMY_TAG)
|
||||
, m_spu2(*this, finder_base::DUMMY_TAG)
|
||||
, m_sio2(*this, finder_base::DUMMY_TAG)
|
||||
, m_icount(0)
|
||||
{
|
||||
}
|
||||
@ -38,11 +38,12 @@ void iop_dma_device::device_start()
|
||||
save_item(NAME(m_channels[channel].m_end), channel);
|
||||
|
||||
save_item(NAME(m_channels[channel].m_addr), channel);
|
||||
save_item(NAME(m_channels[channel].m_block), channel);
|
||||
save_item(NAME(m_channels[channel].m_ctrl), channel);
|
||||
save_item(NAME(m_channels[channel].m_tag_addr), channel);
|
||||
|
||||
save_item(NAME(m_channels[channel].m_size), channel);
|
||||
save_item(NAME(m_channels[channel].m_block), channel);
|
||||
save_item(NAME(m_channels[channel].m_block_count), channel);
|
||||
save_item(NAME(m_channels[channel].m_word_count), channel);
|
||||
save_item(NAME(m_channels[channel].m_count), channel);
|
||||
}
|
||||
|
||||
@ -92,8 +93,8 @@ void iop_dma_device::execute_run()
|
||||
{
|
||||
switch (channel)
|
||||
{
|
||||
case SPU:
|
||||
case SPU2:
|
||||
case SPU_BANK1:
|
||||
case SPU_BANK2:
|
||||
transfer_spu(channel);
|
||||
break;
|
||||
case SIF0:
|
||||
@ -102,6 +103,12 @@ void iop_dma_device::execute_run()
|
||||
case SIF1:
|
||||
transfer_sif1(channel);
|
||||
break;
|
||||
case SIO2_IN:
|
||||
transfer_to_sio2(channel);
|
||||
break;
|
||||
case SIO2_OUT:
|
||||
transfer_from_sio2(channel);
|
||||
break;
|
||||
default:
|
||||
logerror("%s: Attempting to transfer an unimplemented DMA channel (%d)\n", machine().describe_context(), channel);
|
||||
break;
|
||||
@ -164,16 +171,18 @@ void iop_dma_device::transfer_sif0(uint32_t chan)
|
||||
void iop_dma_device::transfer_sif1(uint32_t chan)
|
||||
{
|
||||
channel_t &channel = m_channels[chan];
|
||||
if (channel.m_count)
|
||||
const uint32_t count = channel.count();
|
||||
if (count)
|
||||
{
|
||||
if (m_sif->fifo_depth(1))
|
||||
{
|
||||
const uint32_t data = m_sif->fifo_pop(1);
|
||||
const uint32_t addr = channel.addr();
|
||||
//logerror("%s: sif1 pop value: %08x\n", machine().describe_context(), (uint32_t)data);
|
||||
m_ram[channel.m_addr >> 2] = data;
|
||||
m_ram[addr >> 2] = data;
|
||||
|
||||
channel.m_addr += 4;
|
||||
channel.m_count--;
|
||||
channel.set_addr(addr + 4);
|
||||
channel.set_count(count - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -205,35 +214,77 @@ void iop_dma_device::transfer_sif1(uint32_t chan)
|
||||
void iop_dma_device::transfer_spu(uint32_t chan)
|
||||
{
|
||||
channel_t &channel = m_channels[chan];
|
||||
const uint32_t size = channel.size();
|
||||
if (size)
|
||||
const uint32_t count = channel.count();
|
||||
const bool first_bank = chan == SPU_BANK1;
|
||||
const int bank = first_bank ? 0 : 1;
|
||||
if (count)
|
||||
{
|
||||
printf("Size: %08x\n", size);
|
||||
const uint32_t addr = channel.addr();
|
||||
|
||||
if (chan == SPU)
|
||||
m_spu->dma_write(m_ram[addr >> 2]);
|
||||
else
|
||||
m_spu2->dma_write(m_ram[addr >> 2]);
|
||||
m_spu->dma_write(bank, m_ram[addr >> 2]);
|
||||
|
||||
channel.set_size(size - 1);
|
||||
channel.set_count(count - 1);
|
||||
channel.set_addr(addr + 4);
|
||||
}
|
||||
else if (channel.busy())
|
||||
{
|
||||
channel.set_word_count(0);
|
||||
channel.set_block_count(0);
|
||||
|
||||
m_spu->dma_done(bank);
|
||||
|
||||
//if (first_bank)
|
||||
//m_intc->raise_interrupt(iop_intc_device::INT_SPU);
|
||||
|
||||
transfer_finish(chan);
|
||||
}
|
||||
}
|
||||
|
||||
void iop_dma_device::transfer_to_sio2(uint32_t chan)
|
||||
{
|
||||
channel_t &channel = m_channels[chan];
|
||||
const uint32_t count = channel.count();
|
||||
// TODO: Clock out at correct serial rate
|
||||
if (count)
|
||||
{
|
||||
const uint32_t addr = channel.addr();
|
||||
const uint32_t data = m_ram[addr >> 2];
|
||||
m_sio2->receive((data >> 24) & 0xff);
|
||||
m_sio2->receive((data >> 16) & 0xff);
|
||||
m_sio2->receive((data >> 8) & 0xff);
|
||||
m_sio2->receive(data & 0xff);
|
||||
channel.set_count(count - 1);
|
||||
channel.set_addr(addr + 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.set_count(0);
|
||||
channel.set_block_count(0);
|
||||
channel.set_word_count(0);
|
||||
transfer_finish(SIO2_IN);
|
||||
}
|
||||
}
|
||||
|
||||
if (chan == SPU)
|
||||
m_spu->dma_done();
|
||||
else
|
||||
m_spu2->dma_done();
|
||||
|
||||
if (chan == SPU)
|
||||
{
|
||||
m_intc->raise_interrupt(iop_intc_device::INT_SPU);
|
||||
}
|
||||
|
||||
transfer_finish(chan);
|
||||
void iop_dma_device::transfer_from_sio2(uint32_t chan)
|
||||
{
|
||||
channel_t &channel = m_channels[chan];
|
||||
const uint32_t count = channel.count();
|
||||
// TODO: Clock in at correct serial rate
|
||||
if (count)
|
||||
{
|
||||
const uint32_t addr = channel.addr();
|
||||
uint32_t data = m_sio2->transmit() << 24;
|
||||
data |= m_sio2->transmit() << 16;
|
||||
data |= m_sio2->transmit() << 8;
|
||||
data |= m_sio2->transmit();
|
||||
m_ram[addr >> 2] = data;
|
||||
channel.set_count(count - 1);
|
||||
channel.set_addr(addr + 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.set_block_count(0);
|
||||
channel.set_word_count(0);
|
||||
transfer_finish(SIO2_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +315,7 @@ READ32_MEMBER(iop_dma_device::bank0_r)
|
||||
logerror("%s: bank0_r: channel[%d].addr (%08x & %08x)\n", machine().describe_context(), offset >> 2, ret, mem_mask);
|
||||
break;
|
||||
case 0x04/4: case 0x14/4: case 0x24/4: case 0x34/4: case 0x44/4: case 0x54/4: case 0x64/4:
|
||||
ret = (m_channels[offset >> 2].count() << 16) | m_channels[offset >> 2].size();
|
||||
ret = m_channels[offset >> 2].block();
|
||||
logerror("%s: bank0_r: channel[%d].block (%08x & %08x)\n", machine().describe_context(), offset >> 2, ret, mem_mask);
|
||||
break;
|
||||
case 0x08/4: case 0x18/4: case 0x28/4: case 0x38/4: case 0x48/4: case 0x58/4: case 0x68/4:
|
||||
@ -337,7 +388,7 @@ READ32_MEMBER(iop_dma_device::bank1_r)
|
||||
logerror("%s: bank1_r: channel[%d].addr (%08x & %08x)\n", machine().describe_context(), (offset >> 2) + 8, ret, mem_mask);
|
||||
break;
|
||||
case 0x04/4: case 0x14/4: case 0x24/4: case 0x34/4: case 0x44/4: case 0x54/4: case 0x64/4:
|
||||
ret = (m_channels[(offset >> 2) + 8].count() << 16) | m_channels[(offset >> 2) + 8].size();
|
||||
ret = m_channels[(offset >> 2) + 8].block();
|
||||
logerror("%s: bank1_r: channel[%d].block (%08x & %08x)\n", machine().describe_context(), (offset >> 2) + 8, ret, mem_mask);
|
||||
break;
|
||||
case 0x08/4: case 0x18/4: case 0x28/4: case 0x38/4: case 0x48/4: case 0x58/4: case 0x68/4:
|
||||
@ -421,8 +472,8 @@ void iop_dma_device::set_dpcr(uint32_t data, uint32_t index)
|
||||
|
||||
void iop_dma_device::set_dicr(uint32_t data, uint32_t index)
|
||||
{
|
||||
m_dicr[index] = (m_dicr[index] & (0x7f << 24)) | (data & ~(0x7f << 24));
|
||||
m_dicr[index] &= ~(data & (0x7f << 24));
|
||||
m_dicr[index] = (m_dicr[index] & 0x7f000000) | (data & ~0x7f007fff);
|
||||
m_dicr[index] &= ~(data & 0x7f000000);
|
||||
m_int_ctrl[index].m_mask = (data >> 16) & 0x7f;
|
||||
m_int_ctrl[index].m_status &= ~((data >> 24) & 0x7f);
|
||||
m_int_ctrl[index].m_enabled = BIT(data, 23);
|
||||
@ -440,10 +491,23 @@ void iop_dma_device::channel_t::set_pri_ctrl(uint32_t pri_ctrl)
|
||||
|
||||
void iop_dma_device::channel_t::set_block(uint32_t block, uint32_t mem_mask)
|
||||
{
|
||||
m_block = block;
|
||||
if (mem_mask & 0xffff)
|
||||
m_size = (uint16_t)block;
|
||||
set_block_count(block);
|
||||
if (mem_mask & 0xffff0000)
|
||||
m_count = (uint16_t)(block >> 16);
|
||||
set_word_count(block >> 16);
|
||||
}
|
||||
|
||||
void iop_dma_device::channel_t::set_block_count(uint32_t block_count)
|
||||
{
|
||||
m_block_count = (uint16_t)block_count;
|
||||
m_count = m_block_count * m_word_count;
|
||||
}
|
||||
|
||||
void iop_dma_device::channel_t::set_word_count(uint32_t word_count)
|
||||
{
|
||||
m_word_count = (uint16_t)word_count;
|
||||
m_count = m_block_count * m_word_count;
|
||||
}
|
||||
|
||||
void iop_dma_device::channel_t::set_ctrl(uint32_t ctrl)
|
||||
|
@ -17,20 +17,21 @@
|
||||
#include "emu.h"
|
||||
#include "ps2sif.h"
|
||||
#include "iopintc.h"
|
||||
#include "iopsio2.h"
|
||||
#include "audio/iopspu.h"
|
||||
|
||||
class iop_dma_device : public device_t, public device_execute_interface
|
||||
{
|
||||
public:
|
||||
template <typename T, typename U, typename V, typename W, typename X>
|
||||
iop_dma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&intc_tag, U &&ram_tag, V &&sif_tag, W &&spu_tag, X &&spu2_tag)
|
||||
iop_dma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&intc_tag, U &&ram_tag, V &&sif_tag, W &&spu_tag, X &&sio2_tag)
|
||||
: iop_dma_device(mconfig, tag, owner, clock)
|
||||
{
|
||||
m_intc.set_tag(std::forward<T>(intc_tag));
|
||||
m_ram.set_tag(std::forward<U>(ram_tag));
|
||||
m_sif.set_tag(std::forward<V>(sif_tag));
|
||||
m_spu.set_tag(std::forward<W>(spu_tag));
|
||||
m_spu2.set_tag(std::forward<X>(spu2_tag));
|
||||
m_sio2.set_tag(std::forward<X>(sio2_tag));
|
||||
}
|
||||
|
||||
iop_dma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
@ -46,11 +47,11 @@ public:
|
||||
MDEC_OUT,
|
||||
GPU,
|
||||
CDVD,
|
||||
SPU,
|
||||
SPU_BANK1,
|
||||
PIO,
|
||||
OTC,
|
||||
UNUSED_BANK0,
|
||||
SPU2,
|
||||
SPU_BANK2,
|
||||
UNKNOWN0,
|
||||
SIF0,
|
||||
SIF1,
|
||||
@ -74,27 +75,41 @@ protected:
|
||||
|
||||
public:
|
||||
channel_t()
|
||||
: m_priority(0), m_enabled(false), m_addr(0), m_block(0), m_ctrl(0), m_tag_addr(0), m_size(0), m_count(0)
|
||||
: m_priority(0)
|
||||
, m_enabled(false)
|
||||
, m_busy(false)
|
||||
, m_end(false)
|
||||
, m_addr(0)
|
||||
, m_ctrl(0)
|
||||
, m_tag_addr(0)
|
||||
, m_block(0)
|
||||
, m_block_count(0)
|
||||
, m_word_count(0)
|
||||
, m_count(0)
|
||||
{
|
||||
}
|
||||
|
||||
void set_pri_ctrl(uint32_t pri_ctrl);
|
||||
void set_addr(uint32_t addr) { m_addr = addr; }
|
||||
void set_block(uint32_t block, uint32_t mem_mask);
|
||||
void set_size(uint32_t size) { m_size = size; }
|
||||
void set_block_count(uint32_t block_count);
|
||||
void set_word_count(uint32_t word_count);
|
||||
void set_count(uint32_t count) { m_count = count; }
|
||||
void set_ctrl(uint32_t ctrl);
|
||||
void set_tag_addr(uint32_t tag_addr) { m_tag_addr = tag_addr; }
|
||||
|
||||
bool enabled() const { return m_enabled; }
|
||||
bool end() const { return m_end; }
|
||||
bool busy() const { return m_busy; }
|
||||
|
||||
uint32_t addr() const { return m_addr; }
|
||||
uint32_t block() const { return m_block; }
|
||||
uint32_t size() const { return m_size; }
|
||||
uint32_t count() const { return m_count; }
|
||||
uint32_t ctrl() const { return m_ctrl; }
|
||||
uint32_t tag_addr() const { return m_tag_addr; }
|
||||
bool end() const { return m_end; }
|
||||
|
||||
uint32_t block() const { return m_block; }
|
||||
uint32_t block_count() const { return m_block_count; }
|
||||
uint32_t word_count() const { return m_word_count; }
|
||||
uint32_t count() const { return m_count; }
|
||||
|
||||
protected:
|
||||
uint8_t m_priority;
|
||||
@ -103,11 +118,12 @@ protected:
|
||||
bool m_end;
|
||||
|
||||
uint32_t m_addr;
|
||||
uint32_t m_block;
|
||||
uint32_t m_ctrl;
|
||||
uint32_t m_tag_addr;
|
||||
|
||||
uint32_t m_size;
|
||||
uint32_t m_block;
|
||||
uint32_t m_block_count;
|
||||
uint32_t m_word_count;
|
||||
uint32_t m_count;
|
||||
};
|
||||
|
||||
@ -122,13 +138,15 @@ protected:
|
||||
void transfer_sif0(uint32_t chan);
|
||||
void transfer_sif1(uint32_t chan);
|
||||
void transfer_spu(uint32_t chan);
|
||||
void transfer_to_sio2(uint32_t chan);
|
||||
void transfer_from_sio2(uint32_t chan);
|
||||
void transfer_finish(uint32_t chan);
|
||||
|
||||
required_device<iop_intc_device> m_intc;
|
||||
required_shared_ptr<uint32_t> m_ram;
|
||||
required_device<ps2_sif_device> m_sif;
|
||||
required_device<iop_spu_device> m_spu;
|
||||
required_device<iop_spu_device> m_spu2;
|
||||
required_device<iop_sio2_device> m_sio2;
|
||||
|
||||
int m_icount;
|
||||
|
||||
|
@ -40,7 +40,8 @@ public:
|
||||
INT_DMA = 3,
|
||||
INT_SPU = 9,
|
||||
INT_VB_OFF = 11,
|
||||
INT_TIMER = 16
|
||||
INT_TIMER = 16,
|
||||
INT_SIO2 = 17
|
||||
};
|
||||
|
||||
protected:
|
||||
|
102
src/mame/machine/iopsio2.cpp
Normal file
102
src/mame/machine/iopsio2.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Ryan Holtz
|
||||
/******************************************************************************
|
||||
*
|
||||
* Sony Playstation 2 SIO2 device skeleton
|
||||
*
|
||||
* To Do:
|
||||
* Everything
|
||||
*
|
||||
*/
|
||||
|
||||
#include "iopsio2.h"
|
||||
|
||||
/*static*/ const size_t iop_sio2_device::BUFFER_SIZE = 64; // total guess
|
||||
|
||||
DEFINE_DEVICE_TYPE(SONYIOP_SIO2, iop_sio2_device, "iopsio2", "Playstation 2 SIO2")
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
void iop_sio2_device::device_start()
|
||||
{
|
||||
save_item(NAME(m_ctrl));
|
||||
save_item(NAME(m_receive_buf));
|
||||
save_item(NAME(m_receive_curr));
|
||||
save_item(NAME(m_receive_end));
|
||||
save_item(NAME(m_transmit_buf));
|
||||
save_item(NAME(m_transmit_curr));
|
||||
save_item(NAME(m_transmit_end));
|
||||
}
|
||||
|
||||
void iop_sio2_device::device_reset()
|
||||
{
|
||||
m_ctrl = 0;
|
||||
|
||||
memset(m_receive_buf, 0, BUFFER_SIZE);
|
||||
m_receive_curr = 0;
|
||||
m_receive_end = 0;
|
||||
|
||||
memset(m_transmit_buf, 0, BUFFER_SIZE);
|
||||
m_transmit_curr = 0;
|
||||
m_transmit_end = 0;
|
||||
}
|
||||
|
||||
READ32_MEMBER(iop_sio2_device::read)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
switch (offset)
|
||||
{
|
||||
case 0x68/4: // Control?
|
||||
logerror("%s: read: CTRL %08x & %08x\n", machine().describe_context(), ret, mem_mask);
|
||||
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 0x68/4: // Control?
|
||||
{
|
||||
uint32_t old_ctrl = m_ctrl;
|
||||
m_ctrl = data & CTRL_IRQ;
|
||||
if (old_ctrl != m_ctrl && (m_ctrl & CTRL_IRQ))
|
||||
{
|
||||
m_intc->raise_interrupt(iop_intc_device::INT_SIO2);
|
||||
}
|
||||
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::transmit()
|
||||
{
|
||||
if (m_transmit_curr == m_transmit_end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
uint8_t ret = m_transmit_buf[m_transmit_curr];
|
||||
m_transmit_curr = (m_transmit_curr + 1) & (BUFFER_SIZE-1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iop_sio2_device::receive(uint8_t data)
|
||||
{
|
||||
if (m_receive_end >= BUFFER_SIZE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_receive_buf[m_receive_end] = data;
|
||||
m_receive_end++;
|
||||
}
|
63
src/mame/machine/iopsio2.h
Normal file
63
src/mame/machine/iopsio2.h
Normal file
@ -0,0 +1,63 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Ryan Holtz
|
||||
/******************************************************************************
|
||||
*
|
||||
* Sony Playstation 2 SIO2 device skeleton
|
||||
*
|
||||
* To Do:
|
||||
* Everything
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAME_MACHINE_IOPSIO2_H
|
||||
#define MAME_MACHINE_IOPSIO2_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "emu.h"
|
||||
#include "iopintc.h"
|
||||
|
||||
class iop_sio2_device : public device_t
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
iop_sio2_device(const machine_config &mconfig, const char *tag, device_t *owner, T &&intc_tag)
|
||||
: iop_sio2_device(mconfig, tag, owner, (uint32_t)0)
|
||||
{
|
||||
m_intc.set_tag(std::forward<T>(intc_tag));
|
||||
}
|
||||
|
||||
iop_sio2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
DECLARE_READ32_MEMBER(read);
|
||||
DECLARE_WRITE32_MEMBER(write);
|
||||
|
||||
void receive(uint8_t data);
|
||||
uint8_t transmit();
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
enum
|
||||
{
|
||||
CTRL_IRQ = 0x01,
|
||||
};
|
||||
|
||||
required_device<iop_intc_device> m_intc;
|
||||
|
||||
// HACK: Buffer sizes are guesses.
|
||||
uint8_t m_receive_buf[64];
|
||||
uint8_t m_receive_curr;
|
||||
uint8_t m_receive_end;
|
||||
uint8_t m_transmit_buf[64];
|
||||
uint8_t m_transmit_curr;
|
||||
uint8_t m_transmit_end;
|
||||
uint32_t m_ctrl;
|
||||
|
||||
static const size_t BUFFER_SIZE;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SONYIOP_SIO2, iop_sio2_device)
|
||||
|
||||
#endif // MAME_MACHINE_IOPSIO2_H
|
@ -211,6 +211,10 @@ void ps2_dmac_device::follow_source_tag(uint32_t chan)
|
||||
channel.set_addr(addr);
|
||||
channel.set_tag_addr(channel.tag_addr() + 0x10);
|
||||
break;
|
||||
case ID_NEXT:
|
||||
channel.set_addr(channel.tag_addr() + 0x10);
|
||||
channel.set_tag_addr(addr);
|
||||
break;
|
||||
default:
|
||||
logerror("%s: Unknown DMAtag ID: %d\n", machine().describe_context(), id);
|
||||
break;
|
||||
@ -401,7 +405,7 @@ READ32_MEMBER(ps2_dmac_device::channel_r)
|
||||
break;
|
||||
case 0xc400/8: /* SIF1_CHCR */
|
||||
ret = m_channels[SIF1].chcr();
|
||||
logerror("%s: dmac_channel_r: SIF1_CHCR (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
//logerror("%s: dmac_channel_r: SIF1_CHCR (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
break;
|
||||
case 0xc410/8: /* SIF1_MADR */
|
||||
ret = m_channels[SIF1].addr();
|
||||
@ -413,7 +417,7 @@ READ32_MEMBER(ps2_dmac_device::channel_r)
|
||||
break;
|
||||
case 0xc430/8: /* SIF1_TADR */
|
||||
ret = m_channels[SIF1].tag_addr();
|
||||
logerror("%s: dmac_channel_r: SIF1_TADR (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
//logerror("%s: dmac_channel_r: SIF1_TADR (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
break;
|
||||
case 0xc800/8: /* D7_CHCR */
|
||||
logerror("%s: dmac_channel_r: D7_CHCR (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
|
@ -66,8 +66,8 @@ READ32_MEMBER(ps2_sif_device::ee_r)
|
||||
break;
|
||||
case 6:
|
||||
ret = m_sm_flag;
|
||||
if (ret != 0)
|
||||
logerror("%s: ee_r: SIF slave->master flag (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
//if (ret != 0)
|
||||
//logerror("%s: ee_r: SIF slave->master flag (%08x & %08x)\n", machine().describe_context(), ret, mem_mask);
|
||||
break;
|
||||
case 8:
|
||||
ret = m_ctrl | 0xF0000102;
|
||||
|
Loading…
Reference in New Issue
Block a user