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:
Sandro Ronco 2020-06-11 19:54:45 +02:00
parent 8166769ff9
commit cdb5ca42c6
17 changed files with 7744 additions and 506 deletions

5488
hash/hyperscan_card.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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",

View File

@ -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;

View 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));
}

View 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

View 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);
}
}

View 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

View 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;
}

View 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

View 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);
}
}

View 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

View File

@ -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)

View 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;
}

View 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

View 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;
}
}

View 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