mirror of
https://github.com/holub/mame
synced 2025-06-06 12:53:46 +03:00
Hyperscan updates: [Sandro Ronco]
- Added emulation of the SPG290 CDServo - Added joypad inputs - Added RFID card support - Split SPG290 PPU, Timers and I2C into separate devices - Added a softlist for the RFID cards
This commit is contained in:
parent
8166769ff9
commit
cdb5ca42c6
5488
hash/hyperscan_card.xml
Normal file
5488
hash/hyperscan_card.xml
Normal file
File diff suppressed because it is too large
Load Diff
@ -2907,6 +2907,26 @@ if (MACHINES["SPG2XX"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/spg290_cdservo.h,MACHINES["SPG290"] = true
|
||||
--@src/devices/machine/spg290_timer.h,MACHINES["SPG290"] = true
|
||||
--@src/devices/machine/spg290_i2c.h,MACHINES["SPG290"] = true
|
||||
--@src/devices/machine/spg290_ppu.h,MACHINES["SPG290"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["SPG290"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/spg290_cdservo.cpp",
|
||||
MAME_DIR .. "src/devices/machine/spg290_cdservo.h",
|
||||
MAME_DIR .. "src/devices/machine/spg290_timer.cpp",
|
||||
MAME_DIR .. "src/devices/machine/spg290_timer.h",
|
||||
MAME_DIR .. "src/devices/machine/spg290_i2c.cpp",
|
||||
MAME_DIR .. "src/devices/machine/spg290_i2c.h",
|
||||
MAME_DIR .. "src/devices/machine/spg290_ppu.cpp",
|
||||
MAME_DIR .. "src/devices/machine/spg290_ppu.h",
|
||||
}
|
||||
end
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/stvcd.h,MACHINES["STVCD"] = true
|
||||
|
@ -650,6 +650,7 @@ MACHINES["SMIOC"] = true
|
||||
MACHINES["SEGA_SCU"] = true
|
||||
MACHINES["SMPC"] = true
|
||||
MACHINES["SPG2XX"] = true
|
||||
MACHINES["SPG290"] = true
|
||||
MACHINES["STVCD"] = true
|
||||
MACHINES["SUN4C_MMU"] = true
|
||||
MACHINES["SWTPC8212"] = true
|
||||
@ -3976,6 +3977,10 @@ files {
|
||||
MAME_DIR .. "src/mame/drivers/spg2xx_smarttv.cpp",
|
||||
MAME_DIR .. "src/mame/includes/spg2xx.h",
|
||||
MAME_DIR .. "src/mame/drivers/spg29x.cpp",
|
||||
MAME_DIR .. "src/mame/machine/hyperscan_card.cpp",
|
||||
MAME_DIR .. "src/mame/machine/hyperscan_card.h",
|
||||
MAME_DIR .. "src/mame/machine/hyperscan_ctrl.cpp",
|
||||
MAME_DIR .. "src/mame/machine/hyperscan_ctrl.h",
|
||||
MAME_DIR .. "src/mame/drivers/spg29x_lexibook_jg7425.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/generalplus_gpl16250.cpp",
|
||||
MAME_DIR .. "src/mame/drivers/generalplus_gpl16250_rom.cpp",
|
||||
|
@ -5,6 +5,12 @@
|
||||
Sunplus Technology S+core
|
||||
by Sandro Ronco
|
||||
|
||||
TODO:
|
||||
- unemulated opcodes
|
||||
- irq priority
|
||||
- instruction cycles
|
||||
- cache
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
@ -201,7 +207,7 @@ void score7_cpu_device::execute_run()
|
||||
break;
|
||||
}
|
||||
|
||||
m_icount -= 3; // FIXME: if available use correct cycles per instructions
|
||||
m_icount -= 6; // FIXME: if available use correct cycles per instructions
|
||||
}
|
||||
while (m_icount > 0);
|
||||
}
|
||||
@ -213,19 +219,13 @@ void score7_cpu_device::execute_run()
|
||||
|
||||
void score7_cpu_device::execute_set_input(int inputnum, int state)
|
||||
{
|
||||
switch (inputnum)
|
||||
if (state)
|
||||
{
|
||||
case 0:
|
||||
if(state)
|
||||
standard_irq_callback(inputnum);
|
||||
if (inputnum > 0 && inputnum < 64)
|
||||
{
|
||||
int vector = standard_irq_callback(0);
|
||||
if (vector > 0 && vector < 64)
|
||||
{
|
||||
if((REG_PSR & 0x01) && state)
|
||||
m_pending_interrupt[vector] = true;
|
||||
}
|
||||
m_pending_interrupt[inputnum] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ bool score7_cpu_device::check_condition(uint8_t bc)
|
||||
|
||||
int32_t score7_cpu_device::sign_extend(uint32_t data, uint8_t len)
|
||||
{
|
||||
data &= (1 << len) - 1;
|
||||
data &= (1ULL << len) - 1;
|
||||
uint32_t sign = 1 << (len - 1);
|
||||
return (data ^ sign) - sign;
|
||||
}
|
||||
@ -1149,6 +1149,18 @@ void score7_cpu_device::op_rform1()
|
||||
case 0x05: // t!
|
||||
SET_T(check_condition(rd));
|
||||
break;
|
||||
case 0x08: // sll!
|
||||
m_gpr[rd] = m_gpr[rd] << (m_gpr[ra] & 0x1f);
|
||||
break;
|
||||
case 0x09: // addc!
|
||||
m_gpr[rd] = m_gpr[rd] + m_gpr[ra] + GET_C;
|
||||
break;
|
||||
case 0x0a: // srl!
|
||||
m_gpr[rd] = m_gpr[rd] >> (m_gpr[ra] & 0x1f);
|
||||
break;
|
||||
case 0x0b: // sra!
|
||||
m_gpr[rd] = sign_extend(m_gpr[rd] >> (m_gpr[ra] & 0x1f), 32 - (m_gpr[ra] & 0x1f));
|
||||
break;
|
||||
case 0x0c: // brl!
|
||||
if (check_condition_branch(rd))
|
||||
{
|
||||
@ -1275,7 +1287,12 @@ void score7_cpu_device::op_iform1a()
|
||||
switch(GET_I16_FUNC3(m_op))
|
||||
{
|
||||
case 0x00: // addei!
|
||||
unemulated_op("addei!");
|
||||
if (imm5 & 0x10)
|
||||
m_gpr[rd] -= 1 << (imm5 & 0xf);
|
||||
else
|
||||
m_gpr[rd] += 1 << (imm5 & 0xf);
|
||||
|
||||
// condition flags are invalid after this instruction
|
||||
break;
|
||||
case 0x01: // slli!
|
||||
m_gpr[rd] <<= imm5;
|
||||
|
422
src/devices/machine/spg290_cdservo.cpp
Normal file
422
src/devices/machine/spg290_cdservo.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/*****************************************************************************
|
||||
|
||||
SPG290 CD Servo
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "spg290_cdservo.h"
|
||||
#include "coreutil.h"
|
||||
|
||||
|
||||
#define SPG290_LEADIN_LEN 7500
|
||||
#define SPG290_LEADOUT_LEN 6750
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(SPG290_CDSERVO, spg290_cdservo_device, "spg290_cdservo", "SPG290 CDServo HLE")
|
||||
|
||||
|
||||
spg290_cdservo_device::spg290_cdservo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, SPG290_CDSERVO, tag, owner, clock)
|
||||
, m_cdrom(*this, finder_base::DUMMY_TAG)
|
||||
, m_irq_cb(*this)
|
||||
, m_space_write_cb(*this)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void spg290_cdservo_device::device_start()
|
||||
{
|
||||
m_irq_cb.resolve_safe();
|
||||
m_space_write_cb.resolve_safe();
|
||||
|
||||
m_cdtimer = timer_alloc();
|
||||
m_dsp_memory = std::make_unique<uint32_t[]>(0x10000);
|
||||
|
||||
save_item(NAME(m_addr));
|
||||
save_item(NAME(m_data));
|
||||
save_item(NAME(m_buf_start));
|
||||
save_item(NAME(m_buf_end));
|
||||
save_item(NAME(m_buf_ptr));
|
||||
save_item(NAME(m_speed));
|
||||
save_item(NAME(m_seek_min));
|
||||
save_item(NAME(m_seek_sec));
|
||||
save_item(NAME(m_seek_frm));
|
||||
save_item(NAME(m_seek_lba));
|
||||
save_item(NAME(m_sector_size));
|
||||
save_item(NAME(m_dsp_data));
|
||||
save_item(NAME(m_frame_found));
|
||||
save_item(NAME(m_control1));
|
||||
save_item(NAME(m_skip));
|
||||
save_item(NAME(m_dsp_regs));
|
||||
save_item(NAME(m_tot_sectors));
|
||||
save_item(NAME(m_cur_sector));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void spg290_cdservo_device::device_reset()
|
||||
{
|
||||
m_addr = 0;
|
||||
m_data = 0;
|
||||
m_buf_start = 0;
|
||||
m_buf_end = 0;
|
||||
m_buf_ptr = 0;
|
||||
m_speed = 1;
|
||||
m_seek_min = 0;
|
||||
m_seek_sec = 0;
|
||||
m_seek_frm = 0;
|
||||
m_seek_lba = 0;
|
||||
m_sector_size = 0;
|
||||
m_dsp_data = 0;
|
||||
m_frame_found = false;
|
||||
m_control1 = 0;
|
||||
m_skip = 0;
|
||||
|
||||
std::fill(std::begin(m_dsp_regs), std::end(m_dsp_regs), 0);
|
||||
std::fill_n(m_dsp_memory.get(), 0x10000, 0);
|
||||
|
||||
m_tot_sectors = 0;
|
||||
m_cur_sector = 0;
|
||||
m_qsub = nullptr;
|
||||
|
||||
m_irq_cb(CLEAR_LINE);
|
||||
|
||||
// generate Q subchannel
|
||||
if (m_cdrom.found())
|
||||
{
|
||||
auto *cdrom = m_cdrom->get_cdrom_file();
|
||||
if (cdrom != nullptr)
|
||||
generate_qsub(cdrom);
|
||||
}
|
||||
|
||||
change_status();
|
||||
}
|
||||
|
||||
void spg290_cdservo_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
if (!(m_control1 & 0x04) && m_cur_sector == m_seek_lba + SPG290_LEADIN_LEN)
|
||||
{
|
||||
uint8_t cdbuf[2448] = { 0 };
|
||||
|
||||
if (BIT(m_control0, 15)) // CDDA
|
||||
{
|
||||
cdrom_read_data(m_cdrom->get_cdrom_file(), m_cur_sector - 150 - SPG290_LEADIN_LEN, cdbuf, CD_TRACK_AUDIO);
|
||||
|
||||
for (int i=0; i<2352; i++)
|
||||
{
|
||||
m_space_write_cb(m_buf_ptr++, cdbuf[i]);
|
||||
if (m_buf_ptr > m_buf_end)
|
||||
m_buf_ptr = m_buf_start;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cdrom_read_data(m_cdrom->get_cdrom_file(), m_cur_sector - 150 - SPG290_LEADIN_LEN, cdbuf, CD_TRACK_MODE1_RAW);
|
||||
|
||||
// FIXME: this is required for load iso images
|
||||
if (cdrom_get_track_type(m_cdrom->get_cdrom_file(), m_qsub[m_cur_sector * 12 + 1] - 1) == CD_TRACK_MODE1)
|
||||
{
|
||||
int lba = (bcd_2_dec(cdbuf[12]) * 60 + bcd_2_dec(cdbuf[13])) * 75 + bcd_2_dec(cdbuf[14]);
|
||||
uint32_t msf = lba_to_msf(lba + 150);
|
||||
cdbuf[12] = (msf >> 16) & 0xff;
|
||||
cdbuf[13] = (msf >> 8) & 0xff;
|
||||
cdbuf[14] = (msf >> 0) & 0xff;
|
||||
}
|
||||
|
||||
for (int i=0; i<m_sector_size; i++)
|
||||
{
|
||||
m_space_write_cb(m_buf_ptr++, cdbuf[i]);
|
||||
if (m_buf_ptr > m_buf_end)
|
||||
m_buf_ptr = m_buf_start;
|
||||
}
|
||||
}
|
||||
|
||||
m_seek_lba++;
|
||||
m_frame_found = true;
|
||||
m_irq_cb(ASSERT_LINE);
|
||||
}
|
||||
|
||||
if (m_cur_sector < m_seek_lba + SPG290_LEADIN_LEN and m_cur_sector < m_tot_sectors)
|
||||
m_cur_sector++;
|
||||
}
|
||||
|
||||
uint32_t spg290_cdservo_device::read(offs_t offset, uint32_t mem_mask)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case 0x08:
|
||||
return m_data; // CD servo DSP reply
|
||||
case 0x0c:
|
||||
return 0; // CD servo ready
|
||||
case 0x40:
|
||||
return m_control0;
|
||||
case 0x44:
|
||||
return m_control1;
|
||||
case 0x48:
|
||||
return m_seek_min;
|
||||
case 0x4c:
|
||||
return m_seek_sec;
|
||||
case 0x50:
|
||||
return m_seek_frm;
|
||||
case 0x54:
|
||||
return 0; // sector CRC error flags
|
||||
case 0x60:
|
||||
return m_buf_start;
|
||||
case 0x64:
|
||||
return m_buf_end;
|
||||
case 0x68:
|
||||
return m_buf_ptr;
|
||||
case 0x6c:
|
||||
return m_sector_size;
|
||||
default:
|
||||
logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spg290_cdservo_device::write(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case 0x04: // CD servo DSP command
|
||||
COMBINE_DATA(&m_addr);
|
||||
break;
|
||||
case 0x08: // CD servo DSP data
|
||||
COMBINE_DATA(&m_data);
|
||||
break;
|
||||
case 0x0c: // CD servo DSP exec
|
||||
if (ACCESSING_BITS_0_7)
|
||||
{
|
||||
if (data & 1)
|
||||
servo_cmd_r();
|
||||
else
|
||||
servo_cmd_w();
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
COMBINE_DATA(&m_control0);
|
||||
break;
|
||||
case 0x44:
|
||||
COMBINE_DATA(&m_control1);
|
||||
m_seek_lba = (bcd_2_dec(m_seek_min) * 60 + bcd_2_dec(m_seek_sec)) * 75 + bcd_2_dec(m_seek_frm);
|
||||
m_cur_sector = SPG290_LEADIN_LEN + m_seek_lba; // TODO: seek time
|
||||
break;
|
||||
case 0x48:
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_seek_min = data;
|
||||
break;
|
||||
case 0x4c:
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_seek_sec = data;
|
||||
break;
|
||||
case 0x50:
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_seek_frm = data;
|
||||
break;
|
||||
case 0x60:
|
||||
COMBINE_DATA(&m_buf_start);
|
||||
break;
|
||||
case 0x54:
|
||||
break;
|
||||
case 0x64:
|
||||
COMBINE_DATA(&m_buf_end);
|
||||
break;
|
||||
case 0x68:
|
||||
COMBINE_DATA(&m_buf_ptr);
|
||||
break;
|
||||
case 0x6c:
|
||||
COMBINE_DATA(&m_sector_size);
|
||||
break;
|
||||
default:
|
||||
logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data);
|
||||
}
|
||||
}
|
||||
|
||||
void spg290_cdservo_device::servo_cmd_r()
|
||||
{
|
||||
if (m_addr == 0x079)
|
||||
m_data = 0xe3; // servo status
|
||||
else if (m_addr == 0x07b)
|
||||
m_data = 0x06; // unknown, should be in the range 4-7
|
||||
else if (m_addr == 0x07c)
|
||||
m_data = m_dsp_data >> 8;
|
||||
else if (m_addr == 0x07d)
|
||||
m_data = m_dsp_data;
|
||||
else if (m_addr == 0x082)
|
||||
m_data = 0x04;
|
||||
else if (m_addr == 0x083)
|
||||
m_data = machine().rand() & 0xff;
|
||||
else if (m_addr == 0x307)
|
||||
m_data = m_frame_found;
|
||||
else if (m_addr >= 0x340 && m_addr <= 0x349) // read Q subchannel
|
||||
m_data = m_qsub[m_cur_sector * 12 + (m_addr & 0x0f)];
|
||||
else if (m_addr == 0x34c)
|
||||
m_data = 0x40; // Q subchannel ready
|
||||
else if (m_addr == 0x506)
|
||||
m_data = 0x04; // DSP ready
|
||||
else if (m_addr >= 0x500 && m_addr < 0x50a)
|
||||
m_data = m_dsp_regs[m_addr & 0x0f];
|
||||
else if (m_addr == 0x41f)
|
||||
m_data = 0xff; // if zero cause servo timeout
|
||||
else
|
||||
m_data = 0;
|
||||
}
|
||||
|
||||
|
||||
void spg290_cdservo_device::servo_cmd_w()
|
||||
{
|
||||
if (m_addr == 0x013) // skip n sectors
|
||||
{
|
||||
if (m_data & 0x01)
|
||||
m_cur_sector += (m_data & 0x10) ? -m_skip : m_skip;
|
||||
|
||||
if (m_cur_sector < 0) m_cur_sector = 0;
|
||||
if (m_cur_sector >= m_tot_sectors) m_cur_sector = m_tot_sectors - 1;
|
||||
}
|
||||
else if (m_addr == 0x020) // speed
|
||||
{
|
||||
if (m_speed != 1 << (m_data & 0x03))
|
||||
{
|
||||
m_speed = 1 << (m_data & 0x03);
|
||||
change_status();
|
||||
}
|
||||
}
|
||||
else if (m_addr == 0x030) // Disc ID
|
||||
{
|
||||
// < 10 no disc
|
||||
// < 97 CD-RW
|
||||
// >= 97 CD-ROM
|
||||
if (m_data == 0x13)
|
||||
m_dsp_data = 8;
|
||||
else
|
||||
m_dsp_data = m_qsub ? 100 : 0;
|
||||
}
|
||||
else if (m_addr == 0x032) // DSP version
|
||||
m_dsp_data = 0x0102;
|
||||
else if (m_addr == 0x19a)
|
||||
m_skip = (m_skip & 0x00ff) | (m_data << 8);
|
||||
else if (m_addr == 0x29a)
|
||||
m_skip = (m_skip & 0xff00) | m_data;
|
||||
else if (m_addr == 0x307)
|
||||
m_frame_found = false;
|
||||
else if (m_addr == 0x505) // DSP memory
|
||||
{
|
||||
if ((m_data & 0x0f) == 0x02) // write
|
||||
{
|
||||
// maincpu upload the DSP code in 3 byte chunks
|
||||
uint16_t addr = (m_dsp_regs[0] << 8) | m_dsp_regs[1];
|
||||
m_dsp_memory[addr] = (m_dsp_regs[2] << 16) | (m_dsp_regs[3] << 8) | m_dsp_regs[4];
|
||||
}
|
||||
else if ((m_data & 0x0f) == 0x03) // read
|
||||
{
|
||||
uint16_t addr = (m_dsp_regs[0] << 8) | m_dsp_regs[1];
|
||||
m_dsp_regs[7] = (m_dsp_memory[addr] >> 16) & 0xff;
|
||||
m_dsp_regs[8] = (m_dsp_memory[addr] >> 8) & 0xff;
|
||||
m_dsp_regs[9] = (m_dsp_memory[addr] >> 0) & 0xff;
|
||||
}
|
||||
}
|
||||
else if (m_addr >= 0x500 && m_addr < 0x50a)
|
||||
m_dsp_regs[m_addr & 0x0f] = m_data;
|
||||
}
|
||||
|
||||
|
||||
void spg290_cdservo_device::change_status()
|
||||
{
|
||||
if (m_speed == 0 || !m_cdrom.found() || !m_cdrom->get_cdrom_file())
|
||||
m_cdtimer->adjust(attotime::never);
|
||||
else
|
||||
m_cdtimer->adjust(attotime::from_hz(75 * m_speed), 0, attotime::from_hz(75 * m_speed));
|
||||
}
|
||||
|
||||
void spg290_cdservo_device::add_qsub(int sector, uint8_t addrctrl, uint8_t track, uint8_t index, uint32_t rel_msf, uint32_t abs_msf, uint16_t crc16)
|
||||
{
|
||||
uint8_t *dest = m_qsub.get() + sector * 12;
|
||||
dest[0] = ((addrctrl & 0x0f) << 4) | ((addrctrl & 0xf0) >> 4);
|
||||
dest[1] = track;
|
||||
dest[2] = index;
|
||||
dest[3] = rel_msf >> 16;
|
||||
dest[4] = rel_msf >> 8;
|
||||
dest[5] = rel_msf;
|
||||
dest[6] = 0;
|
||||
dest[7] = abs_msf >> 16;
|
||||
dest[8] = abs_msf >> 8;
|
||||
dest[9] = abs_msf;
|
||||
dest[10] = crc16 >> 8;
|
||||
dest[11] = crc16;
|
||||
}
|
||||
|
||||
void spg290_cdservo_device::generate_qsub(cdrom_file *cdrom)
|
||||
{
|
||||
const cdrom_toc *toc = cdrom_get_toc(cdrom);
|
||||
int numtracks = cdrom_get_last_track(cdrom);
|
||||
uint32_t total_sectors = cdrom_get_track_start(cdrom, numtracks - 1) + toc->tracks[numtracks - 1].frames + 150;
|
||||
|
||||
m_tot_sectors = SPG290_LEADIN_LEN + total_sectors + SPG290_LEADOUT_LEN;
|
||||
m_qsub = std::make_unique<uint8_t[]>(m_tot_sectors * 12);
|
||||
|
||||
int lba = 0;
|
||||
|
||||
// 7500 sectors lead-in
|
||||
for (int s=0; s < SPG290_LEADIN_LEN; s += numtracks + 3)
|
||||
{
|
||||
if (lba < SPG290_LEADIN_LEN) add_qsub(lba++, 0x14, 0, 0xa0, lba_to_msf(s + 0), 1 << 16); // first track number
|
||||
if (lba < SPG290_LEADIN_LEN) add_qsub(lba++, 0x14, 0, 0xa1, lba_to_msf(s + 1), numtracks << 16); // last track number
|
||||
if (lba < SPG290_LEADIN_LEN) add_qsub(lba++, 0x14, 0, 0xa2, lba_to_msf(s + 2), lba_to_msf(total_sectors)); // start time of lead-out
|
||||
|
||||
for(int track = 0; track < numtracks; track++)
|
||||
{
|
||||
uint32_t track_start = cdrom_get_track_start(cdrom, track) + 150;
|
||||
if (lba < SPG290_LEADIN_LEN)
|
||||
add_qsub(lba++, cdrom_get_adr_control(cdrom, track), 0, dec_2_bcd(track + 1), lba_to_msf(s + 3 + track), lba_to_msf(track_start));
|
||||
}
|
||||
}
|
||||
|
||||
// data tracks
|
||||
for(int track = 0; track < numtracks; track++)
|
||||
{
|
||||
uint32_t control = cdrom_get_adr_control(cdrom, track);
|
||||
uint32_t track_start = cdrom_get_track_start(cdrom, track);
|
||||
|
||||
// pregap
|
||||
uint32_t pregap = toc->tracks[track].pregap;
|
||||
|
||||
// first track should have a 150 frames pregap
|
||||
if (track == 0 && toc->tracks[0].pregap == 0)
|
||||
pregap = 150;
|
||||
else if (track != 0 && toc->tracks[0].pregap == 0)
|
||||
track_start += 150;
|
||||
|
||||
for(int s = 0; s < pregap; s++)
|
||||
add_qsub(lba++, control, dec_2_bcd(track + 1), 0, lba_to_msf(s), lba_to_msf(track_start + s));
|
||||
|
||||
track_start += pregap;
|
||||
|
||||
for(int s = 0; s < toc->tracks[track].frames; s++)
|
||||
{
|
||||
// TODO: if present use subcode from CHD
|
||||
add_qsub(lba++, control, dec_2_bcd(track + 1), 1, lba_to_msf(s), lba_to_msf(track_start + s));
|
||||
}
|
||||
|
||||
track_start += toc->tracks[track].frames;
|
||||
|
||||
// postgap
|
||||
for(int s = 0; s < toc->tracks[track].postgap; s++)
|
||||
add_qsub(lba++, control, dec_2_bcd(track + 1), 2, lba_to_msf(s), lba_to_msf(track_start + s));
|
||||
|
||||
track_start += toc->tracks[track].postgap;
|
||||
}
|
||||
|
||||
// 6750 sectors lead-out
|
||||
for(int s = 0; s < SPG290_LEADOUT_LEN; s++)
|
||||
add_qsub(lba++, 0x14, 0xaa, 1, lba_to_msf(s), lba_to_msf(total_sectors + s));
|
||||
}
|
75
src/devices/machine/spg290_cdservo.h
Normal file
75
src/devices/machine/spg290_cdservo.h
Normal file
@ -0,0 +1,75 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
|
||||
|
||||
#ifndef MAME_MACHINE_SPG290_CDSERVO_H
|
||||
#define MAME_MACHINE_SPG290_CDSERVO_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imagedev/chd_cd.h"
|
||||
|
||||
|
||||
class spg290_cdservo_device : public device_t
|
||||
{
|
||||
public:
|
||||
spg290_cdservo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
template <typename T>
|
||||
spg290_cdservo_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&cdrom_tag)
|
||||
: spg290_cdservo_device(mconfig, tag, owner, clock)
|
||||
{
|
||||
m_cdrom.set_tag(std::forward<T>(cdrom_tag));
|
||||
}
|
||||
|
||||
void write(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
uint32_t read(offs_t offset, uint32_t mem_mask);
|
||||
|
||||
auto irq_cb() { return m_irq_cb.bind(); }
|
||||
auto space_write_cb() { return m_space_write_cb.bind(); }
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
|
||||
private:
|
||||
void change_status();
|
||||
void add_qsub(int sector, uint8_t addrctrl, uint8_t track, uint8_t index, uint32_t rel_msf, uint32_t abs_msf, uint16_t crc16=0);
|
||||
void generate_qsub(cdrom_file *cdrom);
|
||||
void servo_cmd_r();
|
||||
void servo_cmd_w();
|
||||
|
||||
optional_device<cdrom_image_device> m_cdrom;
|
||||
devcb_write_line m_irq_cb;
|
||||
devcb_write8 m_space_write_cb;
|
||||
|
||||
emu_timer *m_cdtimer;
|
||||
|
||||
uint32_t m_addr;
|
||||
uint32_t m_data;
|
||||
uint32_t m_buf_start;
|
||||
uint32_t m_buf_end;
|
||||
uint32_t m_buf_ptr;
|
||||
uint8_t m_speed;
|
||||
uint8_t m_seek_min;
|
||||
uint8_t m_seek_sec;
|
||||
uint8_t m_seek_frm;
|
||||
uint32_t m_seek_lba;
|
||||
uint32_t m_sector_size;
|
||||
uint32_t m_dsp_data;
|
||||
bool m_frame_found;
|
||||
uint32_t m_control0;
|
||||
uint32_t m_control1;
|
||||
uint16_t m_skip;
|
||||
uint32_t m_dsp_regs[0x10];
|
||||
|
||||
std::unique_ptr<uint32_t[]> m_dsp_memory;
|
||||
std::unique_ptr<uint8_t[]> m_qsub;
|
||||
uint32_t m_tot_sectors;
|
||||
int m_cur_sector;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SPG290_CDSERVO, spg290_cdservo_device)
|
||||
|
||||
#endif // MAME_MACHINE_SPG290_CDSERVO_H
|
147
src/devices/machine/spg290_i2c.cpp
Normal file
147
src/devices/machine/spg290_i2c.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/*****************************************************************************
|
||||
|
||||
SPG290 I2C
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "spg290_i2c.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(SPG290_I2C, spg290_i2c_device, "spg290_i2c", "SPG290 I2C")
|
||||
|
||||
|
||||
spg290_i2c_device::spg290_i2c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, SPG290_I2C, tag, owner, clock)
|
||||
, m_irq_cb(*this)
|
||||
, m_i2c_read_cb(*this)
|
||||
, m_i2c_write_cb(*this)
|
||||
{
|
||||
}
|
||||
|
||||
void spg290_i2c_device::device_start()
|
||||
{
|
||||
m_irq_cb.resolve_safe();
|
||||
m_i2c_read_cb.resolve_safe(0);
|
||||
m_i2c_write_cb.resolve_safe();
|
||||
m_i2c_timer = timer_alloc();
|
||||
|
||||
save_item(NAME(m_config));
|
||||
save_item(NAME(m_irq_control));
|
||||
save_item(NAME(m_clock_conf));
|
||||
save_item(NAME(m_id));
|
||||
save_item(NAME(m_port_addr));
|
||||
save_item(NAME(m_wdata));
|
||||
save_item(NAME(m_rdata));
|
||||
}
|
||||
|
||||
void spg290_i2c_device::device_reset()
|
||||
{
|
||||
m_config = 0;
|
||||
m_irq_control = 0;
|
||||
m_clock_conf = 0;
|
||||
m_id = 0;
|
||||
m_port_addr = 0;
|
||||
m_wdata = 0;
|
||||
m_rdata = 0;
|
||||
|
||||
m_i2c_timer->adjust(attotime::never);
|
||||
|
||||
m_irq_cb(CLEAR_LINE);
|
||||
}
|
||||
|
||||
void spg290_i2c_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
if (m_config & 0x40)
|
||||
m_rdata = m_i2c_read_cb(m_port_addr);
|
||||
else
|
||||
m_i2c_write_cb(m_port_addr, m_wdata);
|
||||
|
||||
// I2C ack
|
||||
m_config |= (m_config & 7) << 3;
|
||||
|
||||
// I2C IRQ
|
||||
if (m_irq_control & 0x02)
|
||||
{
|
||||
m_irq_control |= 0x01;
|
||||
m_irq_cb(ASSERT_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t spg290_i2c_device::read(offs_t offset, uint32_t mem_mask)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case 0x20: // I2C configuration
|
||||
return m_config;
|
||||
case 0x24: // Interrupt status
|
||||
return m_irq_control;
|
||||
case 0x28: // Clock setting
|
||||
return m_clock_conf;
|
||||
case 0x2c: // ID
|
||||
return m_id;
|
||||
case 0x30: // Port address
|
||||
return m_port_addr;
|
||||
case 0x34: // Write data
|
||||
return m_wdata;
|
||||
case 0x38: // Read data
|
||||
return m_rdata;
|
||||
default:
|
||||
logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spg290_i2c_device::write(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case 0x20: // I2C configuration
|
||||
{
|
||||
COMBINE_DATA(&m_config);
|
||||
|
||||
const auto i2c_clk = attotime::from_hz(clock() / 4 / ((m_clock_conf & 0x3ff) + 1));
|
||||
|
||||
if (m_config & 0x0001) // Start 8
|
||||
m_i2c_timer->adjust(i2c_clk * (2 + 9 * 4));
|
||||
else if (m_config & 0x0002) // Start 16
|
||||
m_i2c_timer->adjust(i2c_clk * (2 + 9 * 5));
|
||||
else if (m_config & 0x0004) // Start N
|
||||
m_i2c_timer->adjust(i2c_clk * (2 + 9 * 4), 0, i2c_clk * (2 + 9 * 4));
|
||||
else if (m_config & 0x0100) // Stop N
|
||||
m_i2c_timer->adjust(attotime::never);
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x24: // Interrupt status
|
||||
COMBINE_DATA(&m_irq_control);
|
||||
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_irq_control &= ~(data & 0x01); // IRQ ack
|
||||
|
||||
if (!(m_irq_control & 0x01))
|
||||
m_irq_cb(CLEAR_LINE);
|
||||
|
||||
break;
|
||||
case 0x28: // Clock setting
|
||||
COMBINE_DATA(&m_clock_conf);
|
||||
break;
|
||||
case 0x2c: // ID
|
||||
COMBINE_DATA(&m_id);
|
||||
break;
|
||||
case 0x30: // Port address
|
||||
COMBINE_DATA(&m_port_addr);
|
||||
break;
|
||||
case 0x34: // Write data
|
||||
COMBINE_DATA(&m_wdata);
|
||||
break;
|
||||
case 0x38: // Read data
|
||||
COMBINE_DATA(&m_rdata);
|
||||
break;
|
||||
default:
|
||||
logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data);
|
||||
}
|
||||
}
|
46
src/devices/machine/spg290_i2c.h
Normal file
46
src/devices/machine/spg290_i2c.h
Normal file
@ -0,0 +1,46 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
|
||||
|
||||
#ifndef MAME_MACHINE_SPG290_I2C_H
|
||||
#define MAME_MACHINE_SPG290_I2C_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class spg290_i2c_device : public device_t
|
||||
{
|
||||
public:
|
||||
spg290_i2c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
void write(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
uint32_t read(offs_t offset, uint32_t mem_mask);
|
||||
|
||||
auto irq_cb() { return m_irq_cb.bind(); }
|
||||
auto i2c_read_cb() { return m_i2c_read_cb.bind(); }
|
||||
auto i2c_write_cb() { return m_i2c_write_cb.bind(); }
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
|
||||
private:
|
||||
devcb_write_line m_irq_cb;
|
||||
devcb_read16 m_i2c_read_cb;
|
||||
devcb_write16 m_i2c_write_cb;
|
||||
|
||||
emu_timer *m_i2c_timer;
|
||||
|
||||
uint32_t m_config;
|
||||
uint32_t m_irq_control;
|
||||
uint32_t m_clock_conf;
|
||||
uint32_t m_id;
|
||||
uint32_t m_port_addr;
|
||||
uint32_t m_wdata;
|
||||
uint32_t m_rdata;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SPG290_I2C, spg290_i2c_device)
|
||||
|
||||
#endif // MAME_MACHINE_SPG290_I2C_H
|
563
src/devices/machine/spg290_ppu.cpp
Normal file
563
src/devices/machine/spg290_ppu.cpp
Normal file
@ -0,0 +1,563 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/*****************************************************************************
|
||||
|
||||
SPG290 PPU
|
||||
|
||||
TODO:
|
||||
- Horizontal and vertical compression
|
||||
- Characters flip
|
||||
- Various graphics glitches
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "spg290_ppu.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(SPG290_PPU, spg290_ppu_device, "spg290_ppu", "SPG290 PPU")
|
||||
|
||||
|
||||
spg290_ppu_device::spg290_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, SPG290_PPU, tag, owner, clock)
|
||||
, m_screen(*this, finder_base::DUMMY_TAG)
|
||||
, m_sprite_palette_ram(*this, "sprite_palette_ram")
|
||||
, m_char_palette_ram(*this, "char_palette_ram")
|
||||
, m_hoffset_ram(*this, "hoffset_ram")
|
||||
, m_voffset_ram(*this, "voffset_ram")
|
||||
, m_sprite_ram(*this, "sprite_ram")
|
||||
, m_vblank_irq_cb(*this)
|
||||
, m_space_read_cb(*this)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void spg290_ppu_device::map(address_map &map)
|
||||
{
|
||||
map(0x0000, 0x00ff).rw(FUNC(spg290_ppu_device::regs_r), FUNC(spg290_ppu_device::regs_w));
|
||||
map(0x1000, 0x17ff).ram().share(m_char_palette_ram);
|
||||
map(0x1800, 0x1fff).ram().share(m_sprite_palette_ram);
|
||||
map(0x2000, 0x27ff).ram().share(m_hoffset_ram);
|
||||
map(0x3000, 0x37ff).ram().share(m_voffset_ram);
|
||||
map(0x4000, 0x4fff).ram().share(m_sprite_ram);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void spg290_ppu_device::device_start()
|
||||
{
|
||||
m_vblank_irq_cb.resolve_safe();
|
||||
m_space_read_cb.resolve_safe(0);
|
||||
|
||||
save_item(NAME(m_control));
|
||||
save_item(NAME(m_sprite_control));
|
||||
save_item(NAME(m_irq_control));
|
||||
save_item(NAME(m_irq_status));
|
||||
save_item(NAME(m_sprite_max));
|
||||
save_item(NAME(m_sprite_buf_start));
|
||||
save_item(NAME(m_blend_mode));
|
||||
save_item(NAME(m_frame_buff));
|
||||
save_item(NAME(m_transrgb));
|
||||
save_item(NAME(m_vcomp_value));
|
||||
save_item(NAME(m_vcomp_offset));
|
||||
save_item(NAME(m_vcomp_step));
|
||||
save_item(NAME(m_irq_timing_v));
|
||||
save_item(NAME(m_irq_timing_h));
|
||||
save_item(NAME(m_vblank_lines));
|
||||
|
||||
save_item(STRUCT_MEMBER(m_txs, control));
|
||||
save_item(STRUCT_MEMBER(m_txs, attribute));
|
||||
save_item(STRUCT_MEMBER(m_txs, posx));
|
||||
save_item(STRUCT_MEMBER(m_txs, posy));
|
||||
save_item(STRUCT_MEMBER(m_txs, nptr));
|
||||
save_item(STRUCT_MEMBER(m_txs, blend));
|
||||
save_item(STRUCT_MEMBER(m_txs, buf_start));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void spg290_ppu_device::device_reset()
|
||||
{
|
||||
m_control = 0;
|
||||
m_sprite_control = 0;
|
||||
m_irq_control = 0;
|
||||
m_irq_status = 0;
|
||||
m_sprite_max = 0;
|
||||
m_sprite_buf_start = 0;
|
||||
m_blend_mode = 0;
|
||||
m_frame_buff[0] = 0;
|
||||
m_frame_buff[1] = 0;
|
||||
m_frame_buff[2] = 0;
|
||||
m_transrgb = 0;
|
||||
m_vcomp_value = 0;
|
||||
m_vcomp_offset = 0;
|
||||
m_vcomp_step = 0;
|
||||
m_irq_timing_v = 0;
|
||||
m_irq_timing_h = 0;
|
||||
m_vblank_lines = 0;
|
||||
memset(m_txs, 0, sizeof(m_txs));
|
||||
|
||||
m_vblank_irq_cb(CLEAR_LINE);
|
||||
}
|
||||
|
||||
|
||||
uint32_t spg290_ppu_device::regs_r(offs_t offset, uint32_t mem_mask)
|
||||
{
|
||||
switch(offset << 2)
|
||||
{
|
||||
case 0x00: // PPU Control
|
||||
return m_control;
|
||||
case 0x04: // Sprite Control
|
||||
return m_sprite_control;
|
||||
case 0x08: // PPU Max Sprites
|
||||
return m_sprite_max;
|
||||
case 0x0c: // blend mode
|
||||
return m_blend_mode;
|
||||
case 0x10: // PPU Max Sprites
|
||||
return m_transrgb;
|
||||
case 0x20: // Text Layer 1 X pos
|
||||
return m_txs[0].posx;
|
||||
case 0x24: // Text Layer 1 Y pos
|
||||
return m_txs[0].posy;
|
||||
case 0x28: // Text Layer 1 attribute
|
||||
return m_txs[0].attribute;
|
||||
case 0x2c: // Text Layer 1 control
|
||||
return m_txs[0].control;
|
||||
case 0x30: // Text Layer 1 number ptr
|
||||
return m_txs[0].nptr;
|
||||
case 0x38: // Text Layer 1 buffer blend
|
||||
return m_txs[0].blend;
|
||||
case 0x3c: // Text Layer 2 X pos
|
||||
return m_txs[1].posx;
|
||||
case 0x40: // Text Layer 2 Y pos
|
||||
return m_txs[1].posy;
|
||||
case 0x44: // Text Layer 2 attribute
|
||||
return m_txs[1].attribute;
|
||||
case 0x48: // Text Layer 2 control
|
||||
return m_txs[1].control;
|
||||
case 0x4c: // Text Layer 2 number ptr
|
||||
return m_txs[1].nptr;
|
||||
case 0x54: // Text Layer 2 buffer blend
|
||||
return m_txs[1].blend;
|
||||
case 0x58: // Text Layer 3 X pos
|
||||
return m_txs[2].posx;
|
||||
case 0x5c: // Text Layer 3 Y pos
|
||||
return m_txs[2].posy;
|
||||
case 0x60: // Text Layer 3 attribute
|
||||
return m_txs[2].attribute;
|
||||
case 0x64: // Text Layer 3 control
|
||||
return m_txs[2].control;
|
||||
case 0x68: // Text Layer 3 number ptr
|
||||
return m_txs[2].nptr;
|
||||
case 0x70: // Tx3 buffer blend
|
||||
return m_txs[2].blend;
|
||||
case 0x74: // vertical comp value
|
||||
return m_vcomp_value;
|
||||
case 0x78: // vertical comp offset
|
||||
return m_vcomp_offset;
|
||||
case 0x7c: // vertical comp step
|
||||
return m_vcomp_step;
|
||||
case 0x80: // PPU IRQ Control
|
||||
return m_irq_control;
|
||||
case 0x84: // PPU IRQ ack
|
||||
return m_irq_status;
|
||||
case 0x88: // IRQTimingV
|
||||
return m_irq_timing_v;
|
||||
case 0x8c: // IRQTimingH
|
||||
return m_irq_timing_h;
|
||||
case 0x90: // vblank lines
|
||||
return m_vblank_lines;
|
||||
case 0x94: // lines count
|
||||
return m_vblank_lines + m_screen->vpos();
|
||||
case 0xa0: case 0xa4: case 0xa8: // Tx1 buffer start
|
||||
return m_txs[0].buf_start[offset & 3];
|
||||
case 0xac: case 0xb0: case 0xb4: // Tx2 buffer start
|
||||
return m_txs[1].buf_start[(offset+1) & 3];
|
||||
case 0xb8: case 0xbc: case 0xc0: // Tx3 buffer starts
|
||||
return m_txs[2].buf_start[(offset+2) & 3];
|
||||
case 0xc4: case 0xc8: case 0xcc: // frame buffer start
|
||||
return m_frame_buff[(offset-1) & 3];
|
||||
case 0xd0: // Sprites buffer start
|
||||
return m_sprite_buf_start;
|
||||
default:
|
||||
logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void spg290_ppu_device::regs_w(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
switch(offset << 2)
|
||||
{
|
||||
case 0x00: // PPU Control
|
||||
COMBINE_DATA(&m_control);
|
||||
break;
|
||||
case 0x04: // Sprite Control
|
||||
COMBINE_DATA(&m_sprite_control);
|
||||
break;
|
||||
case 0x08: // PPU Max Sprites
|
||||
COMBINE_DATA(&m_sprite_max);
|
||||
break;
|
||||
case 0x0c: // blend mode
|
||||
COMBINE_DATA(&m_blend_mode);
|
||||
break;
|
||||
case 0x10: // PPU Max Sprites
|
||||
COMBINE_DATA(&m_transrgb);
|
||||
break;
|
||||
case 0x20: // Text Layer 1 X pos
|
||||
COMBINE_DATA(&m_txs[0].posx);
|
||||
break;
|
||||
case 0x24: // Text Layer 1 Y pos
|
||||
COMBINE_DATA(&m_txs[0].posy);
|
||||
break;
|
||||
case 0x28: // Text Layer 1 attribute
|
||||
COMBINE_DATA(&m_txs[0].attribute);
|
||||
break;
|
||||
case 0x2c: // Text Layer 1 control
|
||||
COMBINE_DATA(&m_txs[0].control);
|
||||
break;
|
||||
case 0x30: // Text Layer 1 number ptr
|
||||
COMBINE_DATA(&m_txs[0].nptr);
|
||||
break;
|
||||
case 0x38: // Text Layer 1 buffer blend
|
||||
COMBINE_DATA(&m_txs[0].blend);
|
||||
break;
|
||||
case 0x3c: // Text Layer 2 X pos
|
||||
COMBINE_DATA(&m_txs[1].posx);
|
||||
break;
|
||||
case 0x40: // Text Layer 2 Y pos
|
||||
COMBINE_DATA(&m_txs[1].posy);
|
||||
break;
|
||||
case 0x44: // Text Layer 2 attribute
|
||||
COMBINE_DATA(&m_txs[1].attribute);
|
||||
break;
|
||||
case 0x48: // Text Layer 2 control
|
||||
COMBINE_DATA(&m_txs[1].control);
|
||||
break;
|
||||
case 0x4c: // Text Layer 2 number ptr
|
||||
COMBINE_DATA(&m_txs[1].nptr);
|
||||
break;
|
||||
case 0x54: // Text Layer 2 buffer blend
|
||||
COMBINE_DATA(&m_txs[1].blend);
|
||||
break;
|
||||
case 0x58: // Text Layer 3 X pos
|
||||
COMBINE_DATA(&m_txs[2].posx);
|
||||
break;
|
||||
case 0x5c: // Text Layer 3 Y pos
|
||||
COMBINE_DATA(&m_txs[2].posy);
|
||||
break;
|
||||
case 0x60: // Text Layer 3 attribute
|
||||
COMBINE_DATA(&m_txs[2].attribute);
|
||||
break;
|
||||
case 0x64: // Text Layer 3 control
|
||||
COMBINE_DATA(&m_txs[2].control);
|
||||
break;
|
||||
case 0x68: // Text Layer 3 number ptr
|
||||
COMBINE_DATA(&m_txs[2].nptr);
|
||||
break;
|
||||
case 0x70: // Tx3 buffer blend
|
||||
COMBINE_DATA(&m_txs[2].blend);
|
||||
break;
|
||||
case 0x74: // vertical comp value
|
||||
COMBINE_DATA(&m_vcomp_value);
|
||||
break;
|
||||
case 0x78: // vertical comp offset
|
||||
COMBINE_DATA(&m_vcomp_offset);
|
||||
break;
|
||||
case 0x7c: // vertical comp step
|
||||
COMBINE_DATA(&m_vcomp_step);
|
||||
break;
|
||||
case 0x80: // PPU IRQ Control
|
||||
COMBINE_DATA(&m_irq_control);
|
||||
m_irq_status &= m_irq_control;
|
||||
break;
|
||||
case 0x84: // PPU IRQ ack
|
||||
if (ACCESSING_BITS_0_7)
|
||||
{
|
||||
m_irq_status &= ~data;
|
||||
|
||||
if (!(m_irq_status & m_irq_control & 0x07))
|
||||
m_vblank_irq_cb(CLEAR_LINE);
|
||||
}
|
||||
break;
|
||||
case 0x88: // IRQTimingV
|
||||
COMBINE_DATA(&m_irq_timing_v);
|
||||
break;
|
||||
case 0x8c: // IRQTimingH
|
||||
COMBINE_DATA(&m_irq_timing_h);
|
||||
break;
|
||||
case 0x90: // vblank lines
|
||||
COMBINE_DATA(&m_vblank_lines);
|
||||
break;
|
||||
case 0xa0: case 0xa4: case 0xa8: // Tx1 buffer start
|
||||
COMBINE_DATA(&m_txs[0].buf_start[offset & 3]);
|
||||
break;
|
||||
case 0xac: case 0xb0: case 0xb4: // Tx2 buffer start
|
||||
COMBINE_DATA(&m_txs[1].buf_start[(offset+1) & 3]);
|
||||
break;
|
||||
case 0xb8: case 0xbc: case 0xc0: // Tx3 buffer starts
|
||||
COMBINE_DATA(&m_txs[2].buf_start[(offset+2) & 3]);
|
||||
break;
|
||||
case 0xc4: case 0xc8: case 0xcc: // frame buffer start
|
||||
COMBINE_DATA(&m_frame_buff[(offset-1) & 3]);
|
||||
break;
|
||||
case 0xd0: // Sprites buffer start
|
||||
COMBINE_DATA(&m_sprite_buf_start);
|
||||
break;
|
||||
default:
|
||||
logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data);
|
||||
}
|
||||
}
|
||||
|
||||
void spg290_ppu_device::screen_vblank(int state)
|
||||
{
|
||||
if (state && (m_irq_control & 0x01)) // VBlanking Start IRQ
|
||||
{
|
||||
m_irq_status |= 0x01;
|
||||
m_vblank_irq_cb(ASSERT_LINE);
|
||||
}
|
||||
else if (!state && (m_irq_control & 0x02)) // VBlanking End IRQ
|
||||
{
|
||||
m_irq_status |= 0x02;
|
||||
m_vblank_irq_cb(ASSERT_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
inline rgb_t spg290_ppu_device::blend_colors(const rgb_t &c0, const rgb_t &c1, uint8_t level)
|
||||
{
|
||||
if (m_blend_mode & 1)
|
||||
{
|
||||
int r = (c0.r() * level / 63) - (c1.r() * (63 - level) / 63);
|
||||
int g = (c0.b() * level / 63) - (c1.b() * (63 - level) / 63);
|
||||
int b = (c0.b() * level / 63) - (c1.b() * (63 - level) / 63);
|
||||
return rgb_t(r, g, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
int r = (c0.r() * level / 63) + (c1.r() * (63 - level) / 63);
|
||||
int g = (c0.b() * level / 63) + (c1.b() * (63 - level) / 63);
|
||||
int b = (c0.b() * level / 63) + (c1.b() * (63 - level) / 63);
|
||||
return rgb_t(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
inline void spg290_ppu_device::argb1555(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t argb, uint8_t blend)
|
||||
{
|
||||
if (!(argb & 0x8000) && cliprect.contains(posx, posy))
|
||||
{
|
||||
rgb_t color = rgb_t(pal5bit(argb >> 10), pal5bit(argb >> 5), pal5bit(argb >> 0));
|
||||
if (blend)
|
||||
color = blend_colors(bitmap.pix32(posy, posx), color, blend);
|
||||
|
||||
bitmap.pix32(posy, posx) = color;
|
||||
}
|
||||
}
|
||||
|
||||
inline void spg290_ppu_device::rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t rgb, uint8_t blend)
|
||||
{
|
||||
if ((!(m_transrgb & 0x10000) || (m_transrgb & 0xffff) != rgb) && cliprect.contains(posx, posy))
|
||||
{
|
||||
rgb_t color = rgb_t(pal5bit(rgb >> 11), pal6bit(rgb >> 5), pal5bit(rgb >> 0));
|
||||
if (blend)
|
||||
color = blend_colors(bitmap.pix32(posy, posx), color, blend);
|
||||
|
||||
bitmap.pix32(posy, posx) = color;
|
||||
}
|
||||
}
|
||||
|
||||
void spg290_ppu_device::blit_sprite(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, uint32_t buf_start)
|
||||
{
|
||||
uint16_t sprite_num = control & 0xffff;
|
||||
uint16_t sprite_x = (control >> 16) & 0x3ff;
|
||||
uint16_t sprite_y = (attribute >> 16) & 0x3ff;
|
||||
uint8_t sprite_hsize = 8 << ((attribute >> 4) & 0x03);
|
||||
uint8_t sprite_vsize = 8 << ((attribute >> 6) & 0x03);
|
||||
uint8_t bit_pixel = ((attribute & 3) + 1) << 1;
|
||||
uint8_t sprite_flip = (attribute >> 2) & 0x03;
|
||||
uint16_t pen_bank = ((attribute >> 8) & 0x1f) * 0x10;
|
||||
uint8_t blend = (attribute & 0x8000) ? (attribute >> 26) & 0x3f : 0;
|
||||
uint8_t pixel_word = 32 / bit_pixel;
|
||||
uint8_t pixel_byte = 8 / bit_pixel;
|
||||
uint8_t word_line = sprite_hsize / pixel_word;
|
||||
uint16_t pen_mask = (1 << bit_pixel) - 1;
|
||||
|
||||
if (sprite_num == 0)
|
||||
return;
|
||||
|
||||
uint32_t sprite_base = buf_start + (sprite_num * (sprite_hsize * sprite_vsize * bit_pixel / 8));
|
||||
|
||||
if (sprite_x > cliprect.max_x) sprite_x = (sprite_x & 0x3ff) - 0x400;
|
||||
if (sprite_y > cliprect.max_y) sprite_y = (sprite_y & 0x3ff) - 0x400;
|
||||
|
||||
for (int y=0; y < sprite_vsize; y++)
|
||||
for (int x=0; x < word_line; x++)
|
||||
{
|
||||
uint32_t data = m_space_read_cb(sprite_base + (y * word_line + x) * 4);
|
||||
|
||||
for (int i=0; i < 4; i++)
|
||||
{
|
||||
for (int b=0; b < pixel_byte; b++)
|
||||
{
|
||||
int bitpos = i * pixel_byte + b;
|
||||
uint16_t pen = (data >> (8 - (b + 1) * bit_pixel)) & pen_mask;
|
||||
uint16_t posx = cliprect.min_x;
|
||||
if (sprite_flip & 0x01)
|
||||
posx += sprite_x + (sprite_hsize - (x * pixel_word + bitpos));
|
||||
else
|
||||
posx += sprite_x + x * pixel_word + bitpos;
|
||||
|
||||
uint16_t posy = cliprect.min_y;
|
||||
if (sprite_flip & 0x02)
|
||||
posy += sprite_y + (sprite_vsize - y);
|
||||
else
|
||||
posy += sprite_y + y;
|
||||
|
||||
argb1555(bitmap, cliprect, posy, posx, m_sprite_palette_ram[(pen_bank + pen) & 0x1ff], blend);
|
||||
}
|
||||
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spg290_ppu_device::blit_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint8_t blend)
|
||||
{
|
||||
int max_x = cliprect.max_x + posx;
|
||||
int max_y = cliprect.max_y + posy;
|
||||
for (int y=0; y < max_y; y++)
|
||||
{
|
||||
int line = (control & 0x04) ? 0 : y;
|
||||
uint32_t tx_start = m_space_read_cb(nptr + line * 4) * 2;
|
||||
|
||||
for (int x=0; x < max_x; x+=2)
|
||||
{
|
||||
uint16_t px = cliprect.min_x + x;
|
||||
uint16_t py = cliprect.min_y + y;
|
||||
uint32_t data = m_space_read_cb(buf_start + tx_start + x * 2);
|
||||
|
||||
for (int b=0; b < 2; b++)
|
||||
{
|
||||
if (control & 0x1000)
|
||||
rgb565(bitmap, cliprect, py - posy, px + b - posx, data & 0xffff, blend);
|
||||
else
|
||||
argb1555(bitmap, cliprect, py - posy, px + b - posx, data & 0xffff, blend);
|
||||
|
||||
data >>= 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spg290_ppu_device::blit_character(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint8_t blend)
|
||||
{
|
||||
uint8_t hsize = 8 << ((attribute >> 4) & 0x03);
|
||||
uint8_t vsize = 8 << ((attribute >> 6) & 0x03);
|
||||
uint8_t bit_pixel = ((attribute & 3) + 1) << 1;
|
||||
uint16_t pen_bank = ((attribute >> 8) & 0x1f) * 0x10;
|
||||
//uint8_t char_flip = (attribute >> 2) & 0x03;
|
||||
uint8_t pixel_word = 32 / bit_pixel;
|
||||
uint8_t chars_line = 1024 / hsize;
|
||||
uint16_t pen_mask = (1 << bit_pixel) - 1;
|
||||
|
||||
int max_x = (cliprect.max_x + posx) / vsize + 1;
|
||||
int max_y = (cliprect.max_y + posy) / hsize + 1;
|
||||
|
||||
for (int y=0; y <= max_y; y++)
|
||||
{
|
||||
for (int x=0; x <= max_x; x++)
|
||||
{
|
||||
int line = (control & 0x04) ? 0 : y;
|
||||
uint32_t addr = line * chars_line + x;
|
||||
uint32_t char_id = m_space_read_cb(nptr + addr * 2);
|
||||
|
||||
if (addr & 1)
|
||||
char_id >>= 16;
|
||||
|
||||
uint32_t char_addr = buf_start + (char_id & 0xffff) * hsize * vsize;
|
||||
|
||||
for (int cy=0; cy < vsize; cy++)
|
||||
{
|
||||
uint32_t line_addr = char_addr + cy * hsize;
|
||||
for (int cx=0; cx < hsize; cx++)
|
||||
{
|
||||
uint32_t data = m_space_read_cb(line_addr + (cx * bit_pixel) / 8);
|
||||
uint16_t px = cliprect.min_x + x * hsize + cx;
|
||||
uint16_t py = cliprect.min_y + y * vsize + cy;
|
||||
uint16_t pen = (data >> ((cx % pixel_word) * bit_pixel)) & pen_mask;
|
||||
uint16_t pix = m_char_palette_ram[(pen_bank + pen) & 0x3ff];
|
||||
|
||||
if (control & 0x1000)
|
||||
rgb565(bitmap, cliprect, py - posy, px - posx, pix, blend);
|
||||
else
|
||||
argb1555(bitmap, cliprect, py - posy, px - posx, pix, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t spg290_ppu_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
|
||||
{
|
||||
bitmap.fill(rgb_t::black(), cliprect);
|
||||
|
||||
if (!(m_control & 0x1000))
|
||||
return 0;
|
||||
|
||||
for (int depth=0; depth<4; depth++)
|
||||
{
|
||||
//if (machine().input().code_pressed(KEYCODE_1_PAD) && depth == 0) continue;
|
||||
//if (machine().input().code_pressed(KEYCODE_2_PAD) && depth == 1) continue;
|
||||
//if (machine().input().code_pressed(KEYCODE_3_PAD) && depth == 2) continue;
|
||||
//if (machine().input().code_pressed(KEYCODE_4_PAD) && depth == 3) continue;
|
||||
|
||||
// draw the bitmap/text layers
|
||||
for (int l=0; l<3; l++)
|
||||
{
|
||||
//if (machine().input().code_pressed(KEYCODE_7_PAD) && l == 0) continue;
|
||||
//if (machine().input().code_pressed(KEYCODE_8_PAD) && l == 1) continue;
|
||||
//if (machine().input().code_pressed(KEYCODE_9_PAD) && l == 2) continue;
|
||||
|
||||
if ((m_txs[l].control & 0x08) && ((m_txs[l].attribute >> 13) & 3) == depth)
|
||||
{
|
||||
uint8_t blend = (m_txs[l].control & 0x0100) ? m_txs[l].blend : 0;
|
||||
|
||||
// posx is 10-bit two's complement
|
||||
int posx = m_txs[l].posx & 0x3ff;
|
||||
if (posx & 0x200)
|
||||
posx = (posx & 0x3ff) - 0x400;
|
||||
|
||||
// posy is 9-bit two's complement
|
||||
int posy = m_txs[l].posy & 0x1ff;
|
||||
if (posy & 0x100)
|
||||
posy = (posy & 0x1ff) - 0x200;
|
||||
|
||||
if (m_txs[l].control & 0x01)
|
||||
blit_bitmap(bitmap, cliprect, m_txs[l].control, m_txs[l].attribute, posy, posx, m_txs[l].nptr, m_txs[l].buf_start[0], blend);
|
||||
else
|
||||
blit_character(bitmap, cliprect, m_txs[l].control, m_txs[l].attribute, posy, posx, m_txs[l].nptr, m_txs[l].buf_start[0], blend);
|
||||
}
|
||||
}
|
||||
|
||||
//if (machine().input().code_pressed(KEYCODE_0_PAD)) continue;
|
||||
|
||||
// draw the sprites
|
||||
if (m_sprite_control & 1)
|
||||
{
|
||||
for (int i=0; i <= (m_sprite_max & 0x1ff); i++)
|
||||
{
|
||||
if (((m_sprite_ram[i * 2 + 1] >> 13) & 3) == depth)
|
||||
blit_sprite(bitmap, cliprect, m_sprite_ram[i * 2], m_sprite_ram[i * 2 + 1], m_sprite_buf_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
85
src/devices/machine/spg290_ppu.h
Normal file
85
src/devices/machine/spg290_ppu.h
Normal file
@ -0,0 +1,85 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
|
||||
|
||||
#ifndef MAME_MACHINE_SPG290_PPU_H
|
||||
#define MAME_MACHINE_SPG290_PPU_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
|
||||
class spg290_ppu_device : public device_t
|
||||
{
|
||||
public:
|
||||
spg290_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
template <typename T>
|
||||
spg290_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, T &&screen_tag)
|
||||
: spg290_ppu_device(mconfig, tag, owner, clock)
|
||||
{
|
||||
m_screen.set_tag(std::forward<T>(screen_tag));
|
||||
}
|
||||
|
||||
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
void screen_vblank(int state);
|
||||
void map(address_map &map);
|
||||
auto vblank_irq_cb() { return m_vblank_irq_cb.bind(); }
|
||||
auto space_read_cb() { return m_space_read_cb.bind(); }
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
uint32_t regs_r(offs_t offset, uint32_t mem_mask);
|
||||
void regs_w(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
|
||||
rgb_t blend_colors(const rgb_t &c0, const rgb_t &c1, uint8_t level);
|
||||
void argb1555(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t argb, uint8_t blend);
|
||||
void rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t rgb, uint8_t blend);
|
||||
void blit_sprite(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, uint32_t buf_start);
|
||||
void blit_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint8_t blend);
|
||||
void blit_character(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint8_t blend);
|
||||
|
||||
private:
|
||||
required_device<screen_device> m_screen;
|
||||
required_shared_ptr<uint32_t> m_sprite_palette_ram;
|
||||
required_shared_ptr<uint32_t> m_char_palette_ram;
|
||||
required_shared_ptr<uint32_t> m_hoffset_ram;
|
||||
required_shared_ptr<uint32_t> m_voffset_ram;
|
||||
required_shared_ptr<uint32_t> m_sprite_ram;
|
||||
devcb_write_line m_vblank_irq_cb;
|
||||
devcb_read32 m_space_read_cb;
|
||||
|
||||
uint32_t m_control;
|
||||
uint32_t m_sprite_control;
|
||||
uint32_t m_irq_control;
|
||||
uint32_t m_irq_status;
|
||||
uint32_t m_sprite_max;
|
||||
uint32_t m_sprite_buf_start;
|
||||
uint32_t m_blend_mode;
|
||||
uint32_t m_frame_buff[3];
|
||||
uint32_t m_transrgb;
|
||||
uint32_t m_vcomp_value;
|
||||
uint32_t m_vcomp_offset;
|
||||
uint32_t m_vcomp_step;
|
||||
uint32_t m_irq_timing_v;
|
||||
uint32_t m_irq_timing_h;
|
||||
uint32_t m_vblank_lines;
|
||||
|
||||
struct spg290_ppu_tx_t
|
||||
{
|
||||
uint32_t control;
|
||||
uint32_t attribute;
|
||||
uint32_t posx;
|
||||
uint32_t posy;
|
||||
uint32_t nptr;
|
||||
uint32_t blend;
|
||||
uint32_t buf_start[3];
|
||||
} m_txs[3];
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SPG290_PPU, spg290_ppu_device)
|
||||
|
||||
#endif // MAME_MACHINE_SPG290_PPU_H
|
156
src/devices/machine/spg290_timer.cpp
Normal file
156
src/devices/machine/spg290_timer.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/*****************************************************************************
|
||||
|
||||
SPG290 Timer
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "spg290_timer.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(SPG290_TIMER, spg290_timer_device, "spg290_timer", "SPG290 Timer")
|
||||
|
||||
|
||||
spg290_timer_device::spg290_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, SPG290_TIMER, tag, owner, clock)
|
||||
, m_irq_cb(*this)
|
||||
{
|
||||
}
|
||||
|
||||
void spg290_timer_device::device_start()
|
||||
{
|
||||
m_irq_cb.resolve_safe();
|
||||
m_tick_timer = timer_alloc();
|
||||
|
||||
save_item(NAME(m_enabled));
|
||||
save_item(NAME(m_control));
|
||||
save_item(NAME(m_control2));
|
||||
save_item(NAME(m_preload));
|
||||
save_item(NAME(m_counter));
|
||||
save_item(NAME(m_ccp));
|
||||
save_item(NAME(m_upcount));
|
||||
}
|
||||
|
||||
void spg290_timer_device::device_reset()
|
||||
{
|
||||
m_enabled = false;
|
||||
m_control = 0;
|
||||
m_control2 = 0;
|
||||
m_preload = 0;
|
||||
m_counter = 0;
|
||||
m_ccp = 0;
|
||||
m_upcount = 0;
|
||||
|
||||
m_tick_timer->adjust(attotime::never);
|
||||
|
||||
m_irq_cb(CLEAR_LINE);
|
||||
}
|
||||
|
||||
void spg290_timer_device::device_clock_changed()
|
||||
{
|
||||
if (m_enabled)
|
||||
m_tick_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock()));
|
||||
else
|
||||
m_tick_timer->adjust(attotime::never);
|
||||
}
|
||||
|
||||
void spg290_timer_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
if (!BIT(m_control, 31))
|
||||
return;
|
||||
|
||||
switch ((m_control2 >> 30) & 0x03)
|
||||
{
|
||||
case 0: // Timer Mode
|
||||
if (m_counter == 0xffff)
|
||||
{
|
||||
m_counter = m_preload & 0xffff;
|
||||
if (BIT(m_control, 27))
|
||||
{
|
||||
m_control |= 0x04000000;
|
||||
m_irq_cb(ASSERT_LINE);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_counter++;
|
||||
break;
|
||||
case 1: // Capture Mode
|
||||
fatalerror("[%s] %s: unemulated timer Capture Mode\n", tag(), machine().describe_context());
|
||||
break;
|
||||
case 2: // Comparison Mode
|
||||
fatalerror("[%s] %s: unemulated timer Comparison Mode\n", tag(), machine().describe_context());
|
||||
break;
|
||||
case 3: // PWM Mode
|
||||
fatalerror("[%s] %s: unemulated timer PWM Mode\n", tag(), machine().describe_context());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void spg290_timer_device::control_w(uint32_t data)
|
||||
{
|
||||
bool enabled = data & 1;
|
||||
if (m_enabled != enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
spg290_timer_device::device_clock_changed();
|
||||
}
|
||||
|
||||
if (data & 2)
|
||||
m_counter = m_preload & 0xffff;
|
||||
}
|
||||
|
||||
uint32_t spg290_timer_device::read(offs_t offset, uint32_t mem_mask)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case 0x00: // Control 1
|
||||
return m_control;
|
||||
case 0x04: // Control 2
|
||||
return m_control2;
|
||||
case 0x08: // Preload
|
||||
return m_preload;
|
||||
case 0x0c: // CCP
|
||||
return m_ccp;
|
||||
case 0x10: // Upcount
|
||||
return m_upcount;
|
||||
default:
|
||||
logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void spg290_timer_device::write(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
switch (offset << 2)
|
||||
{
|
||||
case 0x00: // Control 1
|
||||
COMBINE_DATA(&m_control);
|
||||
|
||||
if (ACCESSING_BITS_24_31)
|
||||
{
|
||||
m_control &= ~(data & 0x04000000); // IRQ ack
|
||||
|
||||
if (!(m_control & 0x04000000))
|
||||
m_irq_cb(CLEAR_LINE);
|
||||
}
|
||||
break;
|
||||
case 0x04: // Control 2
|
||||
COMBINE_DATA(&m_control2);
|
||||
break;
|
||||
case 0x08: // Preload
|
||||
COMBINE_DATA(&m_preload);
|
||||
break;
|
||||
case 0x0c: // CCP
|
||||
COMBINE_DATA(&m_ccp);
|
||||
break;
|
||||
case 0x10: // Upcount
|
||||
COMBINE_DATA(&m_upcount);
|
||||
break;
|
||||
default:
|
||||
logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data);
|
||||
}
|
||||
}
|
43
src/devices/machine/spg290_timer.h
Normal file
43
src/devices/machine/spg290_timer.h
Normal file
@ -0,0 +1,43 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
|
||||
|
||||
#ifndef MAME_MACHINE_SPG290_TIMER_H
|
||||
#define MAME_MACHINE_SPG290_TIMER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class spg290_timer_device : public device_t
|
||||
{
|
||||
public:
|
||||
spg290_timer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
void write(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
uint32_t read(offs_t offset, uint32_t mem_mask);
|
||||
void control_w(uint32_t data);
|
||||
auto irq_cb() { return m_irq_cb.bind(); }
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_clock_changed() override;
|
||||
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
|
||||
private:
|
||||
devcb_write_line m_irq_cb;
|
||||
|
||||
emu_timer *m_tick_timer;
|
||||
|
||||
bool m_enabled;
|
||||
uint16_t m_counter;
|
||||
uint32_t m_control;
|
||||
uint32_t m_control2;
|
||||
uint32_t m_preload;
|
||||
uint32_t m_ccp;
|
||||
uint32_t m_upcount;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(SPG290_TIMER, spg290_timer_device)
|
||||
|
||||
#endif // MAME_MACHINE_SPG290_TIMER_H
|
@ -6,6 +6,18 @@
|
||||
|
||||
08/17/2013 Skeleton driver by Sandro Ronco
|
||||
|
||||
HyperScan TODO:
|
||||
- Various graphics glitches
|
||||
- Sound
|
||||
- X-Men hangs after the first match
|
||||
- USB
|
||||
|
||||
Hyperscan has a hidden test menu that can be accessed with a specific inputs sequence:
|
||||
- During boot press and hold Select + Left Shoulder + Green until 'PLEASE WAIT' is shown on the screen
|
||||
- Press and release Red, Red, Green, Green, Yellow, Blue
|
||||
|
||||
****************************************************************************
|
||||
|
||||
SPG290 Interrupt:
|
||||
|
||||
Vector Source
|
||||
@ -60,11 +72,15 @@
|
||||
|
||||
#include "emu.h"
|
||||
#include "cpu/score/score.h"
|
||||
#include "machine/spg290_cdservo.h"
|
||||
#include "machine/spg290_i2c.h"
|
||||
#include "machine/spg290_ppu.h"
|
||||
#include "machine/spg290_timer.h"
|
||||
#include "machine/hyperscan_card.h"
|
||||
#include "machine/hyperscan_ctrl.h"
|
||||
#include "screen.h"
|
||||
#include "softlist_dev.h"
|
||||
|
||||
#define LOG_SPG290_REGISTER_ACCESS (1)
|
||||
|
||||
|
||||
|
||||
class spg29x_game_state : public driver_device
|
||||
@ -72,7 +88,14 @@ class spg29x_game_state : public driver_device
|
||||
public:
|
||||
spg29x_game_state(const machine_config &mconfig, device_type type, const char *tag) :
|
||||
driver_device(mconfig, type, tag),
|
||||
m_maincpu(*this, "maincpu")
|
||||
m_maincpu(*this, "maincpu"),
|
||||
m_screen(*this, "screen"),
|
||||
m_ppu(*this, "ppu"),
|
||||
m_i2c(*this, "i2c"),
|
||||
m_timers(*this, "timer%u", 0U),
|
||||
m_hyperscan_card(*this, "card"),
|
||||
m_hyperscan_ctrl(*this, "ctrl%u", 0U),
|
||||
m_leds(*this, "led%u", 0U)
|
||||
{ }
|
||||
|
||||
void spg29x(machine_config &config);
|
||||
@ -85,90 +108,32 @@ protected:
|
||||
private:
|
||||
|
||||
virtual void machine_start() override;
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
|
||||
uint32_t spg290_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
uint32_t spg290_regs_r(offs_t offset, uint32_t mem_mask = ~0);
|
||||
void spg290_regs_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
|
||||
void spg290_timers_update();
|
||||
DECLARE_WRITE_LINE_MEMBER(spg290_vblank_irq);
|
||||
inline uint32_t spg290_read_mem(uint32_t offset);
|
||||
inline void spg290_write_mem(uint32_t offset, uint32_t data);
|
||||
void spg290_argb1555(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t argb);
|
||||
void spg290_rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t rgb, uint32_t transrgb);
|
||||
void spg290_blit_sprite(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, uint32_t *palettes, uint32_t buf_start);
|
||||
void spg290_blit_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint32_t transrgb);
|
||||
void spg290_blit_character(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint32_t transrgb);
|
||||
|
||||
void spg290_mem(address_map &map);
|
||||
void spg290_bios_mem(address_map &map);
|
||||
|
||||
static const device_timer_id TIMER_SPG290 = 0;
|
||||
static const device_timer_id TIMER_I2C = 1;
|
||||
void space_byte_w(offs_t offset, uint8_t data) { return m_maincpu->space(AS_PROGRAM).write_byte(offset, data); }
|
||||
uint32_t space_dword_r(offs_t offset) { return m_maincpu->space(AS_PROGRAM).read_dword(offset); }
|
||||
|
||||
struct spg290_miu
|
||||
{
|
||||
uint32_t status;
|
||||
};
|
||||
uint16_t i2c_r(offs_t offset);
|
||||
|
||||
struct spg290_ppu
|
||||
{
|
||||
uint32_t control;
|
||||
uint32_t irq_control;
|
||||
uint32_t irq_status;
|
||||
uint32_t sprite_max;
|
||||
uint32_t sprite_buf_start;
|
||||
uint32_t frame_buff[3];
|
||||
uint32_t palettes[0x200];
|
||||
uint32_t tx_hoffset[0x200];
|
||||
uint32_t tx_hcomp[0x200];
|
||||
uint32_t transrgb;
|
||||
|
||||
struct ppu_spite
|
||||
{
|
||||
uint32_t control;
|
||||
uint32_t attribute;
|
||||
} sprites[0x200];
|
||||
|
||||
struct ppu_tx
|
||||
{
|
||||
uint32_t control;
|
||||
uint32_t attribute;
|
||||
uint32_t posx;
|
||||
uint32_t posy;
|
||||
uint32_t nptr;
|
||||
uint32_t buf_start[3];
|
||||
} txs[3];
|
||||
};
|
||||
|
||||
struct spg290_timer
|
||||
{
|
||||
uint32_t control;
|
||||
uint32_t control2;
|
||||
uint16_t preload;
|
||||
uint16_t counter;
|
||||
};
|
||||
|
||||
struct spg290_i2c
|
||||
{
|
||||
uint32_t config;
|
||||
uint32_t irq_control;
|
||||
uint32_t clock;
|
||||
uint8_t count;
|
||||
uint32_t id;
|
||||
uint32_t port_addr;
|
||||
uint32_t wdata;
|
||||
uint32_t rdata;
|
||||
};
|
||||
|
||||
spg290_miu m_miu;
|
||||
spg290_ppu m_ppu;
|
||||
spg290_timer m_timers[6];
|
||||
spg290_i2c m_i2c;
|
||||
emu_timer * m_update_timer;
|
||||
emu_timer * m_i2c_timer;
|
||||
required_device<screen_device> m_screen;
|
||||
required_device<spg290_ppu_device> m_ppu;
|
||||
required_device<spg290_i2c_device> m_i2c;
|
||||
required_device_array<spg290_timer_device, 6> m_timers;
|
||||
optional_device<hyperscan_card_device> m_hyperscan_card;
|
||||
optional_device_array<hyperscan_ctrl_device, 2> m_hyperscan_ctrl;
|
||||
output_finder<8> m_leds;
|
||||
|
||||
void tve_control_w(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
void gpio_out_w(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
void timers_clk_sel_w(offs_t offset, uint32_t data, uint32_t mem_mask);
|
||||
|
||||
uint16_t m_tve_control;
|
||||
uint8_t m_tve_fade_offset;
|
||||
uint16_t m_gpio_out;
|
||||
};
|
||||
|
||||
class spg29x_nand_game_state : public spg29x_game_state
|
||||
@ -209,438 +174,147 @@ private:
|
||||
|
||||
|
||||
|
||||
|
||||
#if LOG_SPG290_REGISTER_ACCESS
|
||||
static void log_spg290_regs(device_t *device,uint8_t module, uint16_t reg, uint32_t mem_mask, bool write, uint32_t data=0)
|
||||
void spg29x_game_state::timers_clk_sel_w(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
static const char *const modules_name[] =
|
||||
{
|
||||
"CSI", "PPU", "JPG", "TV", "LCD", "SPU", "CD", "MIU", "APBDMA", "BUFCTL","IRQCTL", "GPUBUF", "LDMDMA",
|
||||
"BLNDMA", "TPGBUF", "AHBDEC", "GPIO", "SPI", "SIO", "I2C", "I2S", "UART", "TIMERS/RTC", "WDOG", "SD",
|
||||
"FLASH", "ADC", "USB device", "USB host", "Reserved", "Reserved", "Reserved", "SFTCFG", "CKG", "MP4",
|
||||
"MIU2", "ECC"
|
||||
};
|
||||
auto clock = 27_MHz_XTAL / ((data & 0xff) + 1);
|
||||
|
||||
if (module < 0x25)
|
||||
device->logerror("SPG: %-10s", modules_name[module]);
|
||||
else
|
||||
device->logerror("SPG: mod 0x%02x ", module);
|
||||
|
||||
if (!write)
|
||||
device->logerror(" R 0x%04x", reg);
|
||||
else
|
||||
device->logerror(" W 0x%04x = 0x%08x", reg, data);
|
||||
|
||||
if (mem_mask != 0xffffffffu)
|
||||
device->logerror(" (0x%08x)\n", mem_mask);
|
||||
else
|
||||
device->logerror("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint32_t spg29x_game_state::spg290_regs_r(offs_t offset, uint32_t mem_mask)
|
||||
{
|
||||
uint32_t addr = offset << 2;
|
||||
uint32_t data = 0;
|
||||
|
||||
if (addr == 0x010000) // PPU Control
|
||||
uint32_t mask = 0x100;
|
||||
for(int i=0; i<m_timers.size(); i++)
|
||||
{
|
||||
data = m_ppu.control;
|
||||
}
|
||||
else if (addr == 0x010084) // PPU IRQ Status
|
||||
{
|
||||
data = m_ppu.irq_status;
|
||||
}
|
||||
if (addr == 0x07006c) // MIU Status
|
||||
{
|
||||
data = m_miu.status;
|
||||
}
|
||||
else if ((addr & 0xff0fff) == 0x160000) // Timer X Status
|
||||
{
|
||||
int idx = (offset>>10) & 0x0f;
|
||||
data = m_timers[idx].control;
|
||||
}
|
||||
else if(addr == 0x130024) // I2C interrupt
|
||||
{
|
||||
data = m_i2c.irq_control;
|
||||
}
|
||||
else if(addr == 0x130034) // I2C write data
|
||||
{
|
||||
data = m_i2c.wdata;
|
||||
}
|
||||
else if(addr == 0x130038) // I2C data data
|
||||
{
|
||||
data = m_i2c.rdata;
|
||||
}
|
||||
|
||||
#if LOG_SPG290_REGISTER_ACCESS
|
||||
//else
|
||||
{
|
||||
if (!machine().side_effects_disabled())
|
||||
log_spg290_regs(this,(offset >> 14) & 0xff, (offset<<2) & 0xffff, mem_mask, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_regs_w(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
uint32_t addr = offset << 2;
|
||||
|
||||
if (addr == 0x010000) // PPU Control
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.control);
|
||||
}
|
||||
else if (addr == 0x010008) // PPU Max Sprites
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.sprite_max);
|
||||
}
|
||||
else if (addr == 0x010010) // PPU Max Sprites
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.transrgb);
|
||||
}
|
||||
else if (addr == 0x0100d0) // Sprites buffer start
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.sprite_buf_start);
|
||||
}
|
||||
else if (addr == 0x010020 || addr == 0x01003c || addr == 0x010058) // Text Layers x pos
|
||||
{
|
||||
int idx = (((offset>>3) & 3) | ((offset>>4) & 1)) - 1;
|
||||
COMBINE_DATA(&m_ppu.txs[idx].posx);
|
||||
}
|
||||
else if (addr == 0x010024 || addr == 0x010040 || addr == 0x01005c) // Text Layers y pos
|
||||
{
|
||||
int idx = (((offset>>3) & 3) | ((offset>>4) & 1)) - 1;
|
||||
COMBINE_DATA(&m_ppu.txs[idx].posy);
|
||||
}
|
||||
else if (addr == 0x010028 || addr == 0x010044 || addr == 0x010060) // Text Layers attribute
|
||||
{
|
||||
int idx = ((offset>>3) & 3) - 1;
|
||||
COMBINE_DATA(&m_ppu.txs[idx].attribute);
|
||||
}
|
||||
else if (addr == 0x01002c || addr == 0x010048 || addr == 0x010064) // Text Layers control
|
||||
{
|
||||
int idx = ((offset>>3) & 3) - 1;
|
||||
COMBINE_DATA(&m_ppu.txs[idx].control);
|
||||
}
|
||||
else if (addr == 0x010030 || addr == 0x01004c || addr == 0x010068) // Text Layers number ptr
|
||||
{
|
||||
int idx = ((offset>>3) & 3) - 1;
|
||||
COMBINE_DATA(&m_ppu.txs[idx].nptr);
|
||||
}
|
||||
else if (addr == 0x010080) // PPU IRQ Control
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.irq_control);
|
||||
}
|
||||
else if (addr == 0x010084) // PPU IRQ ack
|
||||
{
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_ppu.irq_status &= ~data;
|
||||
}
|
||||
else if (addr >= 0x0100a0 && addr <= 0x0100a8) // Tx1 buffer start
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.txs[0].buf_start[offset & 3]);
|
||||
}
|
||||
else if (addr >= 0x0100ac && addr <= 0x0100b4) // Tx2 buffer start
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.txs[1].buf_start[(offset+1) & 3]);
|
||||
}
|
||||
else if (addr >= 0x0100b8 && addr <= 0x0100c0) // Tx3 buffer starts
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.txs[2].buf_start[(offset+2) & 3]);
|
||||
}
|
||||
else if ((addr & 0xfff000) == 0x011000) // Palettes
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.palettes[offset & 0x01ff]);
|
||||
}
|
||||
else if ((addr & 0xfff000) == 0x012000) // Tx horizontal offset
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.tx_hoffset[offset & 0x01ff]);
|
||||
}
|
||||
else if ((addr & 0xfff000) == 0x013000) // Tx horizontal compression
|
||||
{
|
||||
COMBINE_DATA(&m_ppu.tx_hcomp[offset & 0x01ff]);
|
||||
}
|
||||
else if ((addr & 0xfff000) == 0x014000) // Sprite Control Registers
|
||||
{
|
||||
int idx = (offset>>1) & 0x1ff;
|
||||
if (offset & 1)
|
||||
COMBINE_DATA(&m_ppu.sprites[idx].attribute);
|
||||
if (data & mask)
|
||||
m_timers[i]->set_clock(32.768_kHz_XTAL);
|
||||
else
|
||||
COMBINE_DATA(&m_ppu.sprites[idx].control);
|
||||
}
|
||||
else if (addr == 0x07005c) // MIU Status
|
||||
{
|
||||
COMBINE_DATA(&m_miu.status);
|
||||
}
|
||||
else if ((addr & 0xff0fff) == 0x160000) // Timer X Control 1
|
||||
{
|
||||
int idx = (offset>>10) & 0x07;
|
||||
COMBINE_DATA(&m_timers[idx].control);
|
||||
m_timers[i]->set_clock(clock);
|
||||
|
||||
if (ACCESSING_BITS_24_31)
|
||||
m_timers[idx].control &= ~(data & 0x04000000); // Timers IRQ ack
|
||||
mask <<= 1;
|
||||
}
|
||||
else if ((addr & 0xff0fff) == 0x160004) // Timer X Control 2
|
||||
{
|
||||
int idx = (offset>>10) & 0x07;
|
||||
COMBINE_DATA(&m_timers[idx].control2);
|
||||
}
|
||||
else if ((addr & 0xff0fff) == 0x160008) // Timer X Preload
|
||||
{
|
||||
int idx = (offset>>10) & 0x07;
|
||||
COMBINE_DATA(&m_timers[idx].preload);
|
||||
}
|
||||
else if (addr == 0x2100e4) // Timer Source Clock Selection
|
||||
{
|
||||
const auto timers_clk = XTAL(27'000'000) / ((data & 0xff) + 1);
|
||||
m_update_timer->adjust(attotime::from_hz(timers_clk), 0, attotime::from_hz(timers_clk));
|
||||
}
|
||||
else if(addr == 0x130020) // I2C configuration
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.config);
|
||||
}
|
||||
else if(addr == 0x130024) // I2C interrupt
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.irq_control);
|
||||
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_i2c.irq_control &= ~(data & 0x00000001); // I2C IRQ ack
|
||||
}
|
||||
else if(addr == 0x130028) // I2C clock setting
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.clock);
|
||||
const auto i2c_clk = XTAL(27'000'000) / ((m_i2c.clock & 0x3ff) + 1);
|
||||
m_i2c_timer->adjust(attotime::from_hz(i2c_clk), 0, attotime::from_hz(i2c_clk));
|
||||
}
|
||||
else if(addr == 0x13002c) // I2C ID
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.id);
|
||||
}
|
||||
else if(addr == 0x130030) // I2C port address
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.port_addr);
|
||||
}
|
||||
else if(addr == 0x130034) // I2C write data
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.wdata);
|
||||
}
|
||||
else if(addr == 0x130038) // I2C data data
|
||||
{
|
||||
COMBINE_DATA(&m_i2c.rdata);
|
||||
}
|
||||
else if(addr == 0x150000) // UART data
|
||||
{
|
||||
if (ACCESSING_BITS_0_7)
|
||||
printf("%c", data & 0xff);
|
||||
}
|
||||
|
||||
#if LOG_SPG290_REGISTER_ACCESS
|
||||
//else
|
||||
{
|
||||
if (!machine().side_effects_disabled())
|
||||
log_spg290_regs(this,(offset >> 14) & 0xff, (offset<<2) & 0xffff, mem_mask, true, data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint32_t spg29x_game_state::spg290_read_mem(uint32_t offset)
|
||||
void spg29x_game_state::tve_control_w(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
return m_maincpu->space(0).read_dword(offset);
|
||||
}
|
||||
|
||||
inline void spg29x_game_state::spg290_write_mem(uint32_t offset, uint32_t data)
|
||||
{
|
||||
return m_maincpu->space(0).write_dword(offset, data);
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_argb1555(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t argb)
|
||||
{
|
||||
if (!(argb & 0x8000) && cliprect.contains(posx, posy))
|
||||
COMBINE_DATA(&m_tve_control);
|
||||
rectangle visarea;
|
||||
switch(m_tve_control & 0xc)
|
||||
{
|
||||
rgb_t color = rgb_t(pal5bit(argb >> 10), pal5bit(argb >> 5), pal5bit(argb >> 0));
|
||||
bitmap.pix32(posy, posx) = color;
|
||||
case 0x0: // QVGA
|
||||
visarea.set(0, 320-1, 0, 240-1);
|
||||
break;
|
||||
case 0x4: // VGA
|
||||
visarea.set(0, 640-1, 0, 480-1);
|
||||
break;
|
||||
case 0x8: // HVGA
|
||||
visarea.set(0, 640-1, 0, 240-1);
|
||||
break;
|
||||
}
|
||||
|
||||
int interlaced = m_tve_control & 1;
|
||||
if (m_tve_control & 2)
|
||||
m_screen->configure(864, 625, visarea, HZ_TO_ATTOSECONDS(27_MHz_XTAL) * 864 * 625 * (interlaced ? 2 : 1)); // PAL
|
||||
else
|
||||
m_screen->configure(858, 525, visarea, HZ_TO_ATTOSECONDS(27_MHz_XTAL) * 858 * 525 * (interlaced ? 2 : 1)); // NTSC
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t rgb, uint32_t transrgb)
|
||||
void spg29x_game_state::gpio_out_w(offs_t offset, uint32_t data, uint32_t mem_mask)
|
||||
{
|
||||
if ((!(transrgb & 0x10000) || (transrgb & 0xffff) != rgb) && cliprect.contains(posx, posy))
|
||||
{
|
||||
rgb_t color = rgb_t(pal5bit(rgb >> 11), pal6bit(rgb >> 5), pal5bit(rgb >> 0));
|
||||
bitmap.pix32(posy, posx) = color;
|
||||
}
|
||||
COMBINE_DATA(&m_gpio_out);
|
||||
|
||||
if (ACCESSING_BITS_0_7)
|
||||
m_hyperscan_card->write(BIT(m_gpio_out,1));
|
||||
|
||||
for(int i=0; i<8; i++)
|
||||
m_leds[i] = BIT(m_gpio_out, 5 + i);
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_blit_sprite(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, uint32_t *palettes, uint32_t buf_start)
|
||||
uint16_t spg29x_game_state::i2c_r(offs_t offset)
|
||||
{
|
||||
uint32_t sprite_base = buf_start + ((control & 0xffff) << 8);
|
||||
uint16_t sprite_x = (control >> 16) & 0x3ff;
|
||||
uint16_t sprite_y = (attribute >> 16) & 0x3ff;
|
||||
uint8_t sprite_hsize = 8 << ((attribute >> 4) & 0x03);
|
||||
uint8_t sprite_vsize = 8 << ((attribute >> 6) & 0x03);
|
||||
uint8_t bit_pixel = ((attribute & 3) + 1) << 1;
|
||||
uint8_t pixel_word = 32 / bit_pixel;
|
||||
uint8_t word_line = sprite_hsize / pixel_word;
|
||||
uint8_t sprite_flip = (attribute >> 2) & 0x03;
|
||||
int port = (offset >> 4) & 0x0f;
|
||||
|
||||
for (int y=0; y < sprite_vsize; y++)
|
||||
for (int x=0; x < word_line; x++)
|
||||
{
|
||||
uint32_t data = spg290_read_mem(sprite_base + (y * pixel_word + x) * 4);
|
||||
if (port < 2)
|
||||
return m_hyperscan_ctrl[port]->read(offset);
|
||||
|
||||
for (int b=0; b < pixel_word; b++)
|
||||
{
|
||||
uint16_t pen = ((data >> (b * bit_pixel)) & ((1 << bit_pixel) - 1));
|
||||
uint16_t posx;
|
||||
if (sprite_flip & 0x01)
|
||||
posx = sprite_x + (sprite_hsize - (x * word_line + b));
|
||||
else
|
||||
posx = sprite_x + x * (word_line) + b;
|
||||
|
||||
uint16_t posy;
|
||||
if (sprite_flip & 0x02)
|
||||
posy = sprite_y + (sprite_vsize - y);
|
||||
else
|
||||
posy = sprite_y + y;
|
||||
|
||||
spg290_argb1555(bitmap, cliprect, posy, posx, palettes[pen]);
|
||||
}
|
||||
}
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_blit_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint32_t transrgb)
|
||||
{
|
||||
for (int y=0; y<512; y++)
|
||||
{
|
||||
int line = (control & 0x04) ? 0 : y;
|
||||
uint32_t tx_start = spg290_read_mem(nptr + (posy + line) * 4);
|
||||
|
||||
for (int x=0; x < 1024>>1; x++)
|
||||
{
|
||||
uint32_t data = spg290_read_mem(buf_start + (tx_start + posx) * 2 + x * 4);
|
||||
|
||||
for (int b=0; b < 2; b++)
|
||||
{
|
||||
uint16_t posx = x * 2 + b;
|
||||
uint16_t posy = y;
|
||||
uint16_t pix = (data >> (b*16)) & 0xffff;
|
||||
|
||||
if (control & 0x0080)
|
||||
spg290_argb1555(bitmap, cliprect, posy, posx, pix);
|
||||
if (control & 0x1000)
|
||||
spg290_rgb565(bitmap, cliprect, posy, posx, pix, transrgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_blit_character(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint32_t transrgb)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
uint32_t spg29x_game_state::spg290_screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
|
||||
{
|
||||
if (m_ppu.control & 0x1000)
|
||||
{
|
||||
for (int depth=0; depth<4; depth++)
|
||||
{
|
||||
// draw the bitmap/text layers
|
||||
for (int l=0; l<3; l++)
|
||||
if ((m_ppu.txs[l].control & 0x08) && ((m_ppu.txs[l].attribute >> 13) & 3) == depth)
|
||||
{
|
||||
if (m_ppu.txs[l].control & 0x01)
|
||||
spg290_blit_bitmap(bitmap, cliprect, m_ppu.txs[l].control, m_ppu.txs[l].attribute, m_ppu.txs[l].posy & 0x1ff, m_ppu.txs[l].posx & 0x1ff, m_ppu.txs[l].nptr, m_ppu.txs[l].buf_start[0], m_ppu.transrgb);
|
||||
else
|
||||
spg290_blit_character(bitmap, cliprect, m_ppu.txs[l].control, m_ppu.txs[l].attribute, m_ppu.txs[l].posy & 0x1ff, m_ppu.txs[l].posx & 0x1ff, m_ppu.txs[l].nptr, m_ppu.txs[l].buf_start[0], m_ppu.transrgb);
|
||||
}
|
||||
m_ppu->screen_update(screen, bitmap, cliprect);
|
||||
|
||||
// draw the sprites
|
||||
for (int i=0; i<=(m_ppu.sprite_max & 0x1ff); i++)
|
||||
{
|
||||
if (((m_ppu.sprites[i].attribute >> 13) & 3) == depth)
|
||||
spg290_blit_sprite(bitmap, cliprect, m_ppu.sprites[i].control, m_ppu.sprites[i].attribute, m_ppu.palettes, m_ppu.sprite_buf_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (m_tve_fade_offset)
|
||||
{
|
||||
bitmap.fill(rgb_t::black(), cliprect);
|
||||
int fade_offset = 255 - m_tve_fade_offset;
|
||||
for (int y=0; y <= cliprect.max_y; y++)
|
||||
for (int x=0; x <= cliprect.max_x; x++)
|
||||
{
|
||||
auto pix = rgb_t(bitmap.pix32(y, x));
|
||||
bitmap.pix32(y, x) = rgb_t(pix.r() * fade_offset / 255, pix.g() * fade_offset / 255, pix.b() * fade_offset / 255);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_timers_update()
|
||||
{
|
||||
for(auto & elem : m_timers)
|
||||
if (elem.control & 0x80000000)
|
||||
{
|
||||
if (((elem.control2 >> 30) & 0x03) == 0x00)
|
||||
{
|
||||
if (elem.counter == 0xffff)
|
||||
{
|
||||
elem.counter = elem.preload;
|
||||
if (elem.control & 0x08000000)
|
||||
{
|
||||
elem.control |= 0x04000000;
|
||||
m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 56); // SCORE7
|
||||
}
|
||||
}
|
||||
else
|
||||
elem.counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: capture, comparison and PWM mode
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(spg29x_game_state::spg290_vblank_irq)
|
||||
{
|
||||
if (state && m_ppu.irq_control & 0x01) // VBlanking Start IRQ
|
||||
{
|
||||
m_ppu.irq_status |= 0x01;
|
||||
m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 53); // SCORE7
|
||||
}
|
||||
else if (!state && m_ppu.irq_control & 0x02) // VBlanking End IRQ
|
||||
{
|
||||
m_ppu.irq_status |= 0x02;
|
||||
m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 53); // SCORE7
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void spg29x_game_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case TIMER_SPG290:
|
||||
spg290_timers_update();
|
||||
break;
|
||||
case TIMER_I2C:
|
||||
if ((m_i2c.config & 0x40) && (m_i2c.config & 0x01))
|
||||
{
|
||||
// TODO: replace with real I2C emulation
|
||||
m_i2c.rdata = 0;
|
||||
|
||||
m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 39); // SCORE7
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void spg29x_game_state::spg290_mem(address_map &map)
|
||||
{
|
||||
map.global_mask(0x1fffffff);
|
||||
map(0x00000000, 0x00ffffff).ram().mirror(0x07000000);
|
||||
map(0x08000000, 0x09ffffff).rw(FUNC(spg29x_game_state::spg290_regs_r), FUNC(spg29x_game_state::spg290_regs_w));
|
||||
|
||||
map(0x08030000, 0x08030003).w(FUNC(spg29x_game_state::tve_control_w)).lr32(NAME([this](uint32_t data) { return m_tve_control; }));
|
||||
map(0x0803000c, 0x0803000f).lw32(NAME([this](uint32_t data) { m_tve_fade_offset = data & 0xff; }));
|
||||
map(0x0807006c, 0x0807006f).lr32(NAME([]() { return 0x01;})); // MUI Status: SDRAM is in the self-refresh mode
|
||||
//map(0x08150000, 0x08150000).lw8(NAME([this](uint8_t data) { printf("%c", data); })); // UART
|
||||
map(0x082100e4, 0x082100e7).w(FUNC(spg29x_game_state::timers_clk_sel_w)); // Timer Source Clock Selection
|
||||
map(0x08240000, 0x0824000f).noprw();
|
||||
|
||||
//map(0x08000000, 0x0800ffff); // CSI
|
||||
map(0x08010000, 0x0801ffff).m("ppu", FUNC(spg290_ppu_device::map));
|
||||
//map(0x08020000, 0x0802ffff); // JPG
|
||||
//map(0x08030000, 0x0803ffff); // TV
|
||||
//map(0x08040000, 0x0804ffff); // LCD
|
||||
//map(0x08050000, 0x0805ffff); // SPU
|
||||
map(0x08060000, 0x0806ffff).rw("cdservo", FUNC(spg290_cdservo_device::read), FUNC(spg290_cdservo_device::write));
|
||||
//map(0x08070000, 0x0807ffff); // MIU
|
||||
//map(0x08080000, 0x0808ffff); // APBDMA
|
||||
//map(0x08090000, 0x0809ffff); // BUFCTL
|
||||
//map(0x080a0000, 0x080affff); // IRQCTL
|
||||
//map(0x080b0000, 0x080bffff); // GPUBUF
|
||||
//map(0x080c0000, 0x080cffff); // LDMDMA
|
||||
//map(0x080d0000, 0x080dffff); // BLNDMA
|
||||
//map(0x080e0000, 0x080effff); // TPGBUF
|
||||
//map(0x080f0000, 0x080fffff); // AHBDEC
|
||||
//map(0x08100000, 0x0810ffff); // GPIO
|
||||
//map(0x08110000, 0x0811ffff); // SPI
|
||||
//map(0x08120000, 0x0812ffff); // SIO
|
||||
map(0x08130000, 0x0813ffff).rw("i2c", FUNC(spg290_i2c_device::read), FUNC(spg290_i2c_device::write));
|
||||
//map(0x08140000, 0x0814ffff); // I2S
|
||||
//map(0x08150000, 0x0815ffff); // UART
|
||||
map(0x08160000, 0x08160fff).rw(m_timers[0], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
|
||||
map(0x08161000, 0x08161fff).rw(m_timers[1], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
|
||||
map(0x08162000, 0x08162fff).rw(m_timers[2], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
|
||||
map(0x08163000, 0x08163fff).rw(m_timers[3], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
|
||||
map(0x08164000, 0x08164fff).rw(m_timers[4], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
|
||||
map(0x08165000, 0x08165fff).rw(m_timers[5], FUNC(spg290_timer_device::read), FUNC(spg290_timer_device::write));
|
||||
//map(0x08166000, 0x08166fff); // RTC
|
||||
//map(0x08170000, 0x0817ffff); // WDOG
|
||||
//map(0x08180000, 0x0818ffff); // SD
|
||||
//map(0x08190000, 0x0819ffff); // FLASH
|
||||
//map(0x081a0000, 0x081affff); // ADC
|
||||
//map(0x081b0000, 0x081bffff); // USB device
|
||||
//map(0x081c0000, 0x081cffff); // USB host
|
||||
//map(0x081d0000, 0x081dffff); // reserved
|
||||
//map(0x081e0000, 0x081effff); // Reserved
|
||||
//map(0x081f0000, 0x081fffff); // reserved
|
||||
//map(0x08200000, 0x0820ffff); // SFTCFG
|
||||
//map(0x08210000, 0x0821ffff); // CKG
|
||||
map(0x0821006c, 0x0821006f).w(m_timers[0], FUNC(spg290_timer_device::control_w));
|
||||
map(0x08210070, 0x08210073).w(m_timers[1], FUNC(spg290_timer_device::control_w));
|
||||
map(0x08210074, 0x08210077).w(m_timers[2], FUNC(spg290_timer_device::control_w));
|
||||
map(0x08210078, 0x0821007b).w(m_timers[3], FUNC(spg290_timer_device::control_w));
|
||||
map(0x0821007c, 0x0821007f).w(m_timers[4], FUNC(spg290_timer_device::control_w));
|
||||
map(0x08210080, 0x08210083).w(m_timers[5], FUNC(spg290_timer_device::control_w));
|
||||
//map(0x08220000, 0x0822ffff); // MP4
|
||||
//map(0x08230000, 0x0823ffff); // MIU2
|
||||
//map(0x08240000, 0x0824ffff); // ECC
|
||||
|
||||
map(0x0a000000, 0x0a003fff).ram(); // internal SRAM
|
||||
map(0x0b000000, 0x0b007fff).rom().region("spg290", 0); // internal ROM
|
||||
}
|
||||
@ -648,8 +322,9 @@ void spg29x_game_state::spg290_mem(address_map &map)
|
||||
void spg29x_game_state::spg290_bios_mem(address_map& map)
|
||||
{
|
||||
spg290_mem(map);
|
||||
map(0x10000000, 0x100fffff).rom().region("bios", 0).mirror(0x0e000000);
|
||||
map(0x11000000, 0x110fffff).rom().region("bios", 0).mirror(0x0e000000);
|
||||
map(0x08200024, 0x08200027).w(FUNC(spg29x_game_state::gpio_out_w)).lr32(NAME([this]() { return m_gpio_out; }));
|
||||
map(0x08200068, 0x0820006b).lr32(NAME([this]() { return m_hyperscan_card->read(); }));
|
||||
map(0x10000000, 0x100fffff).rom().region("bios", 0).mirror(0x0ff00000);
|
||||
}
|
||||
|
||||
/* Input ports */
|
||||
@ -659,16 +334,18 @@ INPUT_PORTS_END
|
||||
|
||||
void spg29x_game_state::machine_start()
|
||||
{
|
||||
m_update_timer = timer_alloc(TIMER_SPG290);
|
||||
m_i2c_timer = timer_alloc(TIMER_I2C);
|
||||
m_leds.resolve();
|
||||
|
||||
save_item(NAME(m_tve_control));
|
||||
save_item(NAME(m_tve_fade_offset));
|
||||
save_item(NAME(m_gpio_out));
|
||||
}
|
||||
|
||||
void spg29x_game_state::machine_reset()
|
||||
{
|
||||
memset(&m_ppu, 0, sizeof(spg290_ppu));
|
||||
memset(&m_miu, 0, sizeof(spg290_miu));
|
||||
memset(&m_i2c, 0, sizeof(m_i2c));
|
||||
m_i2c_timer->adjust(attotime::never, 0, attotime::never);
|
||||
m_tve_control = 0;
|
||||
m_tve_fade_offset = 0;
|
||||
m_gpio_out = 0;
|
||||
|
||||
// disable JTAG
|
||||
m_maincpu->set_state_int(SCORE_CR + 29, 0x20000000);
|
||||
@ -722,25 +399,49 @@ void spg29x_zone3d_game_state::machine_reset()
|
||||
void spg29x_game_state::spg29x(machine_config &config)
|
||||
{
|
||||
/* basic machine hardware */
|
||||
SCORE7(config, m_maincpu, XTAL(27'000'000) * 4); // 108MHz S+core 7
|
||||
SCORE7(config, m_maincpu, 27_MHz_XTAL * 4); // 108MHz S+core 7
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &spg29x_game_state::spg290_mem);
|
||||
|
||||
SOFTWARE_LIST(config, "cd_list").set_original("hyperscan");
|
||||
|
||||
/* video hardware */
|
||||
screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
|
||||
screen.set_refresh_hz(50);
|
||||
screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */
|
||||
screen.set_screen_update(FUNC(spg29x_game_state::spg290_screen_update));
|
||||
screen.set_size(640, 480);
|
||||
screen.set_visarea(0, 640-1, 0, 480-1);
|
||||
screen.screen_vblank().set(FUNC(spg29x_game_state::spg290_vblank_irq));
|
||||
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
|
||||
m_screen->set_raw(27_MHz_XTAL, 858, 0, 640, 525, 0, 480);
|
||||
m_screen->set_screen_update(FUNC(spg29x_game_state::spg290_screen_update));
|
||||
m_screen->screen_vblank().set(m_ppu, FUNC(spg290_ppu_device::screen_vblank));
|
||||
|
||||
for (int i=0; i<6; i++)
|
||||
{
|
||||
SPG290_TIMER(config, m_timers[i], 27_MHz_XTAL);
|
||||
m_timers[i]->irq_cb().set_inputline(m_maincpu, 56);
|
||||
}
|
||||
|
||||
SPG290_PPU(config, m_ppu, 27_MHz_XTAL, m_screen);
|
||||
m_ppu->vblank_irq_cb().set_inputline(m_maincpu, 53);
|
||||
m_ppu->space_read_cb().set(FUNC(spg29x_game_state::space_dword_r));
|
||||
|
||||
spg290_cdservo_device &cdservo(SPG290_CDSERVO(config, "cdservo", 27_MHz_XTAL, "cdrom"));
|
||||
cdservo.irq_cb().set_inputline(m_maincpu, 60);
|
||||
cdservo.space_write_cb().set(FUNC(spg29x_game_state::space_byte_w));
|
||||
|
||||
SPG290_I2C(config, m_i2c, 27_MHz_XTAL);
|
||||
m_i2c->irq_cb().set_inputline(m_maincpu, 39);
|
||||
}
|
||||
|
||||
void spg29x_game_state::hyperscan(machine_config &config)
|
||||
{
|
||||
spg29x(config);
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &spg29x_game_state::spg290_bios_mem);
|
||||
|
||||
m_i2c->i2c_read_cb().set(FUNC(spg29x_game_state::i2c_r));
|
||||
|
||||
CDROM(config, "cdrom").set_interface("cdrom");
|
||||
|
||||
HYPERSCAN_CTRL(config, m_hyperscan_ctrl[0], 0);
|
||||
HYPERSCAN_CTRL(config, m_hyperscan_ctrl[1], 0);
|
||||
|
||||
HYPERSCAN_CARD(config, m_hyperscan_card, 0);
|
||||
|
||||
SOFTWARE_LIST(config, "cd_list").set_original("hyperscan");
|
||||
SOFTWARE_LIST(config, "card_list").set_original("hyperscan_card");
|
||||
}
|
||||
|
||||
void spg29x_nand_game_state::nand_init(int blocksize, int blocksize_stripped)
|
||||
|
275
src/mame/machine/hyperscan_card.cpp
Normal file
275
src/mame/machine/hyperscan_card.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/*****************************************************************************
|
||||
|
||||
Hyperscan RFID card
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "hyperscan_card.h"
|
||||
|
||||
//#define LOG_OUTPUT_FUNC printf
|
||||
//#define VERBOSE 1
|
||||
#include "logmacro.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(HYPERSCAN_CARD, hyperscan_card_device, "hyperscan_card", "Hyperscan RFID card")
|
||||
|
||||
|
||||
hyperscan_card_device::hyperscan_card_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, HYPERSCAN_CARD, tag, owner, clock)
|
||||
, device_image_interface(mconfig, *this)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void hyperscan_card_device::device_start()
|
||||
{
|
||||
save_item(NAME(m_state));
|
||||
save_item(NAME(m_bit_pos));
|
||||
save_item(NAME(m_data));
|
||||
save_item(NAME(m_cmd_buf));
|
||||
save_item(NAME(m_resp_buf));
|
||||
save_item(NAME(m_resp_idx));
|
||||
save_item(NAME(m_last_at));
|
||||
save_item(NAME(m_memory));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void hyperscan_card_device::device_reset()
|
||||
{
|
||||
m_state = 0;
|
||||
m_bit_pos = 0;
|
||||
m_data = 0;
|
||||
m_cmd_buf.clear();
|
||||
m_resp_buf.clear();
|
||||
m_resp_idx = 0;
|
||||
m_last_at = attotime::zero;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------
|
||||
call_load()
|
||||
-------------------------------------------------*/
|
||||
|
||||
image_init_result hyperscan_card_device::call_load()
|
||||
{
|
||||
if (fread(m_memory, sizeof(m_memory)) != sizeof(m_memory))
|
||||
return image_init_result::FAIL;
|
||||
|
||||
battery_load(m_memory, sizeof(m_memory), nullptr);
|
||||
|
||||
return image_init_result::PASS;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
call_unload()
|
||||
-------------------------------------------------*/
|
||||
|
||||
void hyperscan_card_device::call_unload()
|
||||
{
|
||||
memset(m_memory, 0, sizeof(m_memory));
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
CRC
|
||||
-------------------------------------------------*/
|
||||
uint16_t hyperscan_card_device::calc_crc(std::vector<uint8_t> const &data)
|
||||
{
|
||||
uint16_t crc = 0xffffU;
|
||||
|
||||
for (auto b : data)
|
||||
{
|
||||
b ^= crc & 0xff;
|
||||
b ^= b << 4;
|
||||
|
||||
crc = (crc >> 8) ^ ((uint16_t)b << 8) ^ ((uint16_t)b << 3) ^ ((uint16_t)b >> 4);
|
||||
}
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
|
||||
void hyperscan_card_device::resp_add_byte(uint8_t data, bool add_parity)
|
||||
{
|
||||
int parity = 1;
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
int bit = BIT(data, i);
|
||||
m_resp_buf.push_back(bit);
|
||||
parity ^= bit;
|
||||
}
|
||||
|
||||
if (add_parity)
|
||||
m_resp_buf.push_back(parity);
|
||||
}
|
||||
|
||||
void hyperscan_card_device::make_resp(std::vector<uint8_t> const &data, bool add_crc)
|
||||
{
|
||||
m_resp_idx = 0;
|
||||
m_resp_buf.clear();
|
||||
|
||||
resp_add_byte(0x00, false);
|
||||
resp_add_byte(0x80, false);
|
||||
|
||||
for (auto b : data)
|
||||
resp_add_byte(b, true);
|
||||
|
||||
if (add_crc)
|
||||
{
|
||||
uint16_t crc = calc_crc(data);
|
||||
resp_add_byte(crc, true);
|
||||
resp_add_byte(crc >> 8, true);
|
||||
}
|
||||
}
|
||||
|
||||
void hyperscan_card_device::check_command()
|
||||
{
|
||||
std::vector<uint8_t> buf;
|
||||
|
||||
if (m_cmd_buf.size() == 1 && m_cmd_buf[0] == 0x26) // REQA
|
||||
{
|
||||
LOG("RFID: REQA\n");
|
||||
buf.clear();
|
||||
buf.push_back(0x00); // ATQA0
|
||||
buf.push_back(0x00); // ATQA1
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, false);
|
||||
}
|
||||
else if (m_cmd_buf.size() == 1 && m_cmd_buf[0] == 0x52) // WUPA
|
||||
{
|
||||
LOG("RFID: WUPA\n");
|
||||
buf.clear();
|
||||
buf.push_back(0x00); // ATQA0
|
||||
buf.push_back(0x00); // ATQA1
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, false);
|
||||
}
|
||||
else if (m_cmd_buf.size() == 9 && m_cmd_buf[0] == 0x78) // RID
|
||||
{
|
||||
LOG("RFID: RID\n");
|
||||
buf.clear();
|
||||
buf.push_back(0x11); // HR0
|
||||
buf.push_back(0x00); // HR1
|
||||
for (int i=0; i<4; i++)
|
||||
buf.push_back(m_memory[i]); // UID
|
||||
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, true);
|
||||
}
|
||||
else if (m_cmd_buf.size() == 9 && m_cmd_buf[0] == 0x01) // READ
|
||||
{
|
||||
if (m_cmd_buf[1] >= sizeof(m_memory)) m_cmd_buf[1] %= sizeof(m_memory);
|
||||
LOG("RFID: READ %x %02x\n", m_cmd_buf[1], m_memory[m_cmd_buf[1]]);
|
||||
buf.clear();
|
||||
buf.push_back(m_cmd_buf[1]); // ADD
|
||||
buf.push_back(m_memory[m_cmd_buf[1]]); // DATA
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, true);
|
||||
}
|
||||
else if (m_cmd_buf.size() == 9 && m_cmd_buf[0] == 0x00) // RALL
|
||||
{
|
||||
LOG("RFID: RALL\n");
|
||||
buf.clear();
|
||||
buf.push_back(0x11); // HR0
|
||||
buf.push_back(0x00); // HR1
|
||||
for (int i=0; i<sizeof(m_memory); i++)
|
||||
buf.push_back(m_memory[i]);
|
||||
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, true);
|
||||
}
|
||||
else if (m_cmd_buf.size() == 9 && m_cmd_buf[0] == 0x53) // WRITE-E
|
||||
{
|
||||
LOG("RFID: WRITE-E %x %02x\n", m_cmd_buf[1], m_cmd_buf[2]);
|
||||
if (m_cmd_buf[1] >= sizeof(m_memory)) m_cmd_buf[1] %= sizeof(m_memory);
|
||||
|
||||
if (m_memory[m_cmd_buf[1]] != m_cmd_buf[2])
|
||||
{
|
||||
m_memory[m_cmd_buf[1]] = m_cmd_buf[2];
|
||||
battery_save(m_memory, sizeof(m_memory));
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
buf.push_back(m_cmd_buf[1]); // ADD
|
||||
buf.push_back(m_cmd_buf[2]); // DATA
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, true);
|
||||
}
|
||||
else if (m_cmd_buf.size() == 9 && m_cmd_buf[0] == 0x1a) // WRITE-NE
|
||||
{
|
||||
LOG("RFID: WRITE-NE %x %02x\n", m_cmd_buf[1], m_cmd_buf[2]);
|
||||
if (m_cmd_buf[1] >= sizeof(m_memory)) m_cmd_buf[1] %= sizeof(m_memory);
|
||||
|
||||
if (m_memory[m_cmd_buf[1]] != (m_memory[m_cmd_buf[1]] | m_cmd_buf[2]))
|
||||
{
|
||||
m_memory[m_cmd_buf[1]] |= m_cmd_buf[2];
|
||||
battery_save(m_memory, sizeof(m_memory));
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
buf.push_back(m_cmd_buf[1]); // ADD
|
||||
buf.push_back(m_cmd_buf[2]); // DATA
|
||||
m_cmd_buf.clear();
|
||||
make_resp(buf, true);
|
||||
}
|
||||
}
|
||||
|
||||
void hyperscan_card_device::write(int state)
|
||||
{
|
||||
if (m_state == state || !is_loaded())
|
||||
return;
|
||||
|
||||
m_state = state;
|
||||
|
||||
// 13.56MHz
|
||||
int clk = 13560000 / (machine().time() - m_last_at).as_hz();
|
||||
m_last_at = machine().time();
|
||||
|
||||
if (state)
|
||||
return;
|
||||
|
||||
if (clk < 1000)
|
||||
{
|
||||
if (clk < 500)
|
||||
m_data &= ~(1 << m_bit_pos);
|
||||
else
|
||||
m_data |= 1 << m_bit_pos;
|
||||
|
||||
m_bit_pos++;
|
||||
|
||||
if ((m_cmd_buf.size() == 0 && m_bit_pos == 7) || m_bit_pos == 8)
|
||||
{
|
||||
if (m_cmd_buf.size() == 0)
|
||||
m_data &= 0x7f;
|
||||
|
||||
m_cmd_buf.push_back(m_data);
|
||||
m_data = 0;
|
||||
m_bit_pos = 0;
|
||||
check_command();
|
||||
}
|
||||
}
|
||||
else if (clk < 5000)
|
||||
{
|
||||
m_bit_pos = 0;
|
||||
m_data = 0;
|
||||
if (m_resp_idx <= m_resp_buf.size() * 2)
|
||||
m_resp_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
int hyperscan_card_device::read()
|
||||
{
|
||||
if (is_loaded() && (m_resp_idx >> 1) < m_resp_buf.size())
|
||||
return m_resp_buf[m_resp_idx >> 1] ^ (m_resp_idx & 1);
|
||||
|
||||
return 0;
|
||||
}
|
58
src/mame/machine/hyperscan_card.h
Normal file
58
src/mame/machine/hyperscan_card.h
Normal file
@ -0,0 +1,58 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
|
||||
|
||||
#ifndef MAME_MACHINE_HYPERSCAN_CARD_H
|
||||
#define MAME_MACHINE_HYPERSCAN_CARD_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "softlist_dev.h"
|
||||
|
||||
class hyperscan_card_device : public device_t,
|
||||
public device_image_interface
|
||||
{
|
||||
public:
|
||||
hyperscan_card_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
int read();
|
||||
void write(int state);
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// image-level overrides
|
||||
virtual image_init_result call_load() override;
|
||||
virtual void call_unload() override;
|
||||
virtual iodevice_t image_type() const noexcept override { return IO_MEMCARD; }
|
||||
virtual bool is_readable() const noexcept override { return true; }
|
||||
virtual bool is_writeable() const noexcept override { return true; }
|
||||
virtual bool is_creatable() const noexcept override { return false; }
|
||||
virtual bool must_be_loaded() const noexcept override { return false; }
|
||||
virtual bool is_reset_on_load() const noexcept override { return false; }
|
||||
virtual const char *image_interface() const noexcept override { return "hyperscan_card"; }
|
||||
virtual const char *file_extensions() const noexcept override { return "bin"; }
|
||||
|
||||
// device_image_interface implementation
|
||||
virtual const software_list_loader &get_software_list_loader() const override { return image_software_list_loader::instance(); }
|
||||
|
||||
private:
|
||||
uint16_t calc_crc(std::vector<uint8_t> const &data);
|
||||
void resp_add_byte(uint8_t data, bool add_parity);
|
||||
void make_resp(std::vector<uint8_t> const &data, bool add_crc);
|
||||
void check_command();
|
||||
|
||||
int m_state;
|
||||
int m_bit_pos;
|
||||
uint8_t m_data;
|
||||
std::vector<uint8_t> m_cmd_buf;
|
||||
std::vector<uint8_t> m_resp_buf;
|
||||
uint8_t m_resp_idx;
|
||||
attotime m_last_at;
|
||||
uint8_t m_memory[120];
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(HYPERSCAN_CARD, hyperscan_card_device)
|
||||
|
||||
#endif // MAME_MACHINE_HYPERSCAN_CARD_H
|
106
src/mame/machine/hyperscan_ctrl.cpp
Normal file
106
src/mame/machine/hyperscan_ctrl.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
/*****************************************************************************
|
||||
|
||||
Hyperscan controller
|
||||
|
||||
The controller contains a SPC11122A MCU and a Winbond W55AD808 ADC.
|
||||
|
||||
Pinouts:
|
||||
|
||||
3 4
|
||||
2 I 5
|
||||
1 I 6
|
||||
|
||||
1: Brown : GND
|
||||
2: Yellow : DATA
|
||||
3: Grey : CLK
|
||||
4: Green : ENABLE
|
||||
5: Blue : RESET
|
||||
6: Red : VCC 3.3v
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "hyperscan_ctrl.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(HYPERSCAN_CTRL, hyperscan_ctrl_device, "hyperscan_ctrl", "Hyperscan controller HLE")
|
||||
|
||||
|
||||
hyperscan_ctrl_device::hyperscan_ctrl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, HYPERSCAN_CTRL, tag, owner, clock)
|
||||
, m_inputs(*this, "IN.%u", 0U)
|
||||
{
|
||||
}
|
||||
|
||||
static INPUT_PORTS_START( hyperscan_ctrl )
|
||||
PORT_START("IN.0")
|
||||
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Blue")
|
||||
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_START) PORT_NAME("Start")
|
||||
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_SELECT) PORT_NAME("Select")
|
||||
PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Left Shoulder")
|
||||
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Right Shoulder")
|
||||
PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Left Trigger")
|
||||
PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON8) PORT_NAME("Right Trigger")
|
||||
PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_UNUSED)
|
||||
|
||||
PORT_START("IN.1")
|
||||
PORT_BIT(0x1f, IP_ACTIVE_HIGH, IPT_UNUSED)
|
||||
PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Yellow")
|
||||
PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Red")
|
||||
PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Green")
|
||||
|
||||
PORT_START("IN.2")
|
||||
PORT_BIT(0xff, 0x7f, IPT_AD_STICK_Y) PORT_NAME("Analog Y") PORT_REVERSE PORT_SENSITIVITY(100) PORT_KEYDELTA(30) PORT_CENTERDELTA(50)
|
||||
|
||||
PORT_START("IN.3")
|
||||
PORT_BIT(0xff, 0x7f, IPT_AD_STICK_X) PORT_NAME("Analog X") PORT_REVERSE PORT_SENSITIVITY(100) PORT_KEYDELTA(30) PORT_CENTERDELTA(50)
|
||||
INPUT_PORTS_END
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void hyperscan_ctrl_device::device_start()
|
||||
{
|
||||
save_item(NAME(m_input_data));
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void hyperscan_ctrl_device::device_reset()
|
||||
{
|
||||
m_input_data[0] = m_input_data[1] = m_input_data[2] = m_input_data[3] = 0;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// input_ports - device-specific input ports
|
||||
//-------------------------------------------------
|
||||
|
||||
ioport_constructor hyperscan_ctrl_device::device_input_ports() const
|
||||
{
|
||||
return INPUT_PORTS_NAME( hyperscan_ctrl );
|
||||
}
|
||||
|
||||
|
||||
uint16_t hyperscan_ctrl_device::read(offs_t offset)
|
||||
{
|
||||
int port = (offset >> 4) & 0x0f;
|
||||
int addr = offset & 0x0f;
|
||||
|
||||
if (addr < 4)
|
||||
{
|
||||
m_input_data[addr] = m_inputs[addr]->read();
|
||||
return m_input_data[addr];
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is used to validate the inputs
|
||||
return ((m_input_data[0] + m_input_data[1] + m_input_data[2] + m_input_data[3]) << 2) | port;
|
||||
}
|
||||
}
|
31
src/mame/machine/hyperscan_ctrl.h
Normal file
31
src/mame/machine/hyperscan_ctrl.h
Normal file
@ -0,0 +1,31 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Sandro Ronco
|
||||
|
||||
|
||||
#ifndef MAME_MACHINE_HYPERSCAN_CTRL_H
|
||||
#define MAME_MACHINE_HYPERSCAN_CTRL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class hyperscan_ctrl_device : public device_t
|
||||
{
|
||||
public:
|
||||
hyperscan_ctrl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
uint16_t read(offs_t offset);
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// optional information overrides
|
||||
virtual ioport_constructor device_input_ports() const override;
|
||||
|
||||
private:
|
||||
required_ioport_array<4> m_inputs;
|
||||
uint8_t m_input_data[4];
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(HYPERSCAN_CTRL, hyperscan_ctrl_device)
|
||||
|
||||
#endif // MAME_MACHINE_HYPERSCAN_CTRL_H
|
Loading…
Reference in New Issue
Block a user