jaleco/jaleco_vj_qtaro.cpp: Added preliminary King Qtaro PCI video decoder card device.

Video decoding is not implemented yet.
This commit is contained in:
987123879113 2024-02-20 03:56:43 +11:00 committed by Vas Crabb
parent 78b9d5d98b
commit 1b9cdb3f59
5 changed files with 673 additions and 5 deletions

View File

@ -48,6 +48,7 @@ jaleco_vj_pc_device::jaleco_vj_pc_device(const machine_config &mconfig, const ch
device_t(mconfig, JALECO_VJ_PC, tag, owner, clock),
device_mixer_interface(mconfig, *this, 2),
m_maincpu(*this, "maincpu"),
m_king_qtaro(*this, "pci:08.0"),
m_sound(*this, "isa1:vj_sound"),
m_is_steppingstage(false)
{
@ -125,6 +126,9 @@ void jaleco_vj_pc_device::device_add_mconfig(machine_config &config)
// TODO: pci:07.3 0x30401106 VIA VT83C572, VT86C586/A/B Power Management Controller
JALECO_VJ_KING_QTARO(config, m_king_qtaro, 0);
m_king_qtaro->set_bus_master_space(m_maincpu, AS_PROGRAM); // FIXME: remove this workaround when PCI framework grows bus mastering support
// TODO: Should actually be pci:0a.0 but it only shows a black screen
PCI_SLOT(config, "pci:2", pci_cards, 16, 1, 2, 3, 0, "virgedx").set_fixed(true);

View File

@ -5,6 +5,7 @@
#pragma once
#include "jaleco_vj_qtaro.h"
#include "jaleco_vj_sound.h"
#include "cpu/i386/i386.h"
@ -23,6 +24,9 @@ public:
void comm_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) { m_sound->comm_w(offset, data, mem_mask); }
void ymz_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) { m_sound->ymz_w(offset, data, mem_mask); }
template <int DeviceId> void video_mix_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) { m_king_qtaro->video_mix_w<DeviceId>(offset, data); }
void video_control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) { m_king_qtaro->video_control_w(offset, data); }
protected:
virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD;
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
@ -35,6 +39,7 @@ private:
void boot_state_w(uint8_t data);
required_device<pentium_device> m_maincpu;
required_device<jaleco_vj_king_qtaro_device> m_king_qtaro;
required_device<jaleco_vj_isa16_sound_device> m_sound;
bool m_is_steppingstage;
};

View File

@ -0,0 +1,527 @@
// license:BSD-3-Clause
// copyright-holders:windyfairy
/*
King Qtaro PCI card and Qtaro subboards for VJ
Main board ("King Qtaro"):
No markings
----------------------------------------------
| |
| |
| |
| FLEX |
| CN3 CN2 |
| |
| |
| LS6201 |
| |
| |-------| |-| |------------|
------ ------------- ------
LS6201 - LSI LS6201 027 9850KX001 PCI Local Bus Interface
FLEX - Altera Flex EPF10K10QC208-4 DAB239813
CN2, CN3 - 68-pin connectors
Information about the LS6201:
https://web.archive.org/web/20070912033617/http://www.lsisys.co.jp/prod/ls6201/ls6201.htm
https://web.archive.org/web/20001015203836/http://www.lsisys.co.jp:80/prod/LS6201.pdf
The King Qtaro board appears to be a custom spec ordered from LSI Systems and shares some
layout similarities to the LS6201 evaluation card offered by LSI Systems.
JALECO VJ-98347
MADE IN JAPAN
EB-00-20125-0
(Front)
-----------------------------------
| CN2 CN4 CN6 |
| |
| |
| |
| |
| |
| CN1 CN3 CN5 |
-----------------------------------
CN1/2/3/4/5/6 - 80-pin connectors
(Back)
-----------------------------------
| CN9 |
| |
| U4 |
| CN7 U1 CN8 |
| U2 |
| U3 |
| |
-----------------------------------
U1 - ?
U2, U3 - (Unpopulated)
U4 - HM87AV LM3940IS
CN7, CN8 - 68-pin connectors. Connects to CN3, CN2 on main board
JALECO VJ-98341 ("Qtaro")
MADE IN JAPAN
EB-00-20124-0
----------------------------------------
| |
| |----------| |
| | | D4516161 |
| | FLEX | U2 CN3 |
| | | CN4 |
| |----------| U1 |
| |
| |
----------------------------------------
FLEX - Altera FLEX EPF10K30AQC240-3 DBA439849
D4516161 - NEC uPD4516161AG5-A80 512K x 16-bit x 2-banks (16MBit) SDRAM (SSOP50)
U1, U2 - LVX244
CN3 - 40 pin connector (connects to VJ-98342)
CN4 - 40 pin connector (connects to VJ-98342)
Hardware testing confirms that the Qtaro board is responsible for mixing the sprites from the subboard
with the movies from the PC side.
On real hardware, when the CN3 ribbon cables for two monitors going into the subboard are swapped
but CN4 is left in its proper ordering, the sprites will appear based on the placement of the ribbon
cable on the subboard. The movies are still in the correct ordering.
TODO: Timing of when the videos start and stop is not accurate
VJ needs all of the videos to start and end at the same time, and if one video finishes before the others then
it will try to stop *all* of the video data streams at the same time on the PC side.
Setting the buffer size <= 0x8000 fixes it to some degree because the DMAs can't run ahead of each other too far,
but there's an issue with Stepping Stage where it'll just not send data for a small period causing videos to pause
and then go out of sync (???).
The MPEG decoder is on the FPGA on each of the Qtaro boards. The implementation here doesn't try to handle
any potential quirks with the decoding. I think a more accurate to the FPGA version implementation would
be required to make things work exactly like the real hardware, because the FPGA version seems to be able
to start the video stream sooner, and it does not seem to reset any kind of state between videos.
To the last point, I have multiple video recordings of Stepping Stage showing garbage from the previous
video at the very start of a new video for a few frames. DMA timings are almost surely incorrect.
TODO: The last decoded video frame can show up at inappropriate times
Color bar check background in VJ for example enables the video stream so will show the last decoded frame sometimes.
*/
#include "emu.h"
#include "jaleco_vj_qtaro.h"
#include <algorithm>
#define LOG_VIDEO (1U << 1)
#define LOG_DMA (1U << 2)
#define LOG_VERBOSE_VIDEO (1U << 3)
#define LOG_VERBOSE_DMA (1U << 4)
#define LOG_VERBOSE_VIDEO_DATA (1U << 5)
// #define VERBOSE (LOG_VIDEO | LOG_DMA | LOG_VERBOSE_VIDEO | LOG_VERBOSE_DMA)
// #define LOG_OUTPUT_STREAM std::cout
#include "logmacro.h"
DEFINE_DEVICE_TYPE(JALECO_VJ_QTARO, jaleco_vj_qtaro_device, "jaleco_vj_qtaro", "Jaleco VJ Qtaro Subboard")
DEFINE_DEVICE_TYPE(JALECO_VJ_KING_QTARO, jaleco_vj_king_qtaro_device, "jaleco_vj_king_qtaro", "Jaleco VJ King Qtaro PCI Device")
static constexpr unsigned DMA_BURST_SIZE = 128U;
#define DMA_TIMER_PERIOD attotime::from_hz(33'000'000 / 64)
jaleco_vj_qtaro_device::jaleco_vj_qtaro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, JALECO_VJ_QTARO, tag, owner, clock)
{
}
void jaleco_vj_qtaro_device::device_start()
{
save_item(NAME(m_int));
save_item(NAME(m_mix_level));
}
void jaleco_vj_qtaro_device::device_reset()
{
m_int = 0;
m_mix_level = 0;
}
void jaleco_vj_qtaro_device::video_mix_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
if (data != m_mix_level) {
LOGMASKED(LOG_VIDEO, "[%s] video_mix_w %04x\n", tag(), data);
}
m_mix_level = data;
}
uint8_t jaleco_vj_qtaro_device::reg_r(offs_t offset)
{
return m_int;
}
void jaleco_vj_qtaro_device::reg_w(offs_t offset, uint8_t data)
{
// Bit 7 is set when starting DMA for an entirely new video, then unset when video is ended
if (BIT(data, 7) && !BIT(m_int, 7)) {
LOGMASKED(LOG_VIDEO, "[%s] DMA transfer thread started %02x\n", tag(), data);
} else if (!BIT(data, 7) && BIT(m_int, 7)) {
LOGMASKED(LOG_VIDEO, "[%s] DMA transfer thread ended %02x\n", tag(), data);
}
m_int = data;
}
uint8_t jaleco_vj_qtaro_device::reg2_r(offs_t offset)
{
// WriteStream cleanup function will loop until this returns 0.
// Probably relates to DMA or video playback state.
return 0;
}
uint32_t jaleco_vj_qtaro_device::reg3_r(offs_t offset)
{
// 0x20 is some kind of default state. Relates to DMA or video playback.
// If this value is 0x40 then the code sets it back to 0x20 during the WriteStream cleanup function.
return 0x20;
}
void jaleco_vj_qtaro_device::reg3_w(offs_t offset, uint32_t data)
{
}
void jaleco_vj_qtaro_device::write(uint8_t *data, uint32_t len)
{
LOGMASKED(LOG_VERBOSE_VIDEO_DATA, "[%s] video data write %02x\n", tag(), data);
}
/////////////////////////////////////////////
void jaleco_vj_king_qtaro_device::video_control_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// Speed controlled by IRQ 4 on subboard CPU
LOGMASKED(LOG_VIDEO, "video_control_w %04x\n", data);
// Bits 0, 2, 4 change when a video frame should or shouldn't be decoded
// Bits 1, 3, 5 are always set?
}
uint32_t jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_status_r(offs_t offset)
{
// Tested when uploading Qtaro firmware
// 0x100 is set when busy and will keep looping until it's not 0x100
return 0;
}
void jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_status_w(offs_t offset, uint32_t data)
{
// Set to 0x80000020 when uploading Qtaro firmware
}
uint32_t jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_r(offs_t offset)
{
// Should only return 1 when the Qtaro subboard firmware is finished writing.
// Returning 1 on the first byte will cause it to stop uploading the firmware,
// then it'll write 3 0xffs and on the last 0xff if it sees 1 then it thinks it finished
// uploading the firmware successfully.
return 1;
}
void jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_w(offs_t offset, uint32_t data)
{
}
uint32_t jaleco_vj_king_qtaro_device::fpga_firmware_status_r(offs_t offset)
{
// Tested when uploading King Qtaro firmware
// 0x100 is set when busy and will keep looping until it's not 0x100
return 0;
}
void jaleco_vj_king_qtaro_device::fpga_firmware_status_w(offs_t offset, uint32_t data)
{
// Set to 0x80000020 when uploading King Qtaro firmware
}
uint32_t jaleco_vj_king_qtaro_device::fpga_firmware_r(offs_t offset)
{
// Should only return 1 when the King Qtaro firmware is finished writing.
// Returning 1 on the first byte will cause it to stop uploading the firmware,
// then it'll write 3 0xffs and on the last 0xff if it sees 1 then it thinks it finished
// uploading the firmware successfully.
return 1;
}
void jaleco_vj_king_qtaro_device::fpga_firmware_w(offs_t offset, uint32_t data)
{
}
uint8_t jaleco_vj_king_qtaro_device::event_io_mask_r(offs_t offset)
{
return m_event_io_mask[offset];
}
void jaleco_vj_king_qtaro_device::event_io_mask_w(offs_t offset, uint8_t data)
{
m_event_io_mask[offset] = data;
}
uint8_t jaleco_vj_king_qtaro_device::event_unk_r(offs_t offset)
{
return m_event_unk[offset];
}
void jaleco_vj_king_qtaro_device::event_unk_w(offs_t offset, uint8_t data)
{
m_event_unk[offset] = data;
}
uint8_t jaleco_vj_king_qtaro_device::event_io_r(offs_t offset)
{
uint8_t r = m_event_io[offset];
if (offset == 0)
r |= 0b111; // Some kind of status flag for each Qtaro board? Must be 1 after writing FPGA firmware
return r;
}
void jaleco_vj_king_qtaro_device::event_io_w(offs_t offset, uint8_t data)
{
m_event_io[offset] = data;
}
uint32_t jaleco_vj_king_qtaro_device::event_r(offs_t offset)
{
// 0x200 = Read event, based on debug strings (What was read? DMA data?)
return m_event;
}
void jaleco_vj_king_qtaro_device::event_w(offs_t offset, uint32_t data)
{
m_event &= ~data;
}
uint32_t jaleco_vj_king_qtaro_device::event_mask_r(offs_t offset)
{
return m_event_mask;
}
void jaleco_vj_king_qtaro_device::event_mask_w(offs_t offset, uint32_t data)
{
m_event_mask = data;
}
uint32_t jaleco_vj_king_qtaro_device::int_r(offs_t offset)
{
auto r = m_int & ~0x10;
if (m_dma_running[0] || m_dma_running[1] || m_dma_running[2]) {
// The only time 0x10 is referenced is when ending WriteStream for the individual Qtaro devices.
// All 3 of the WriteStream cleanup functions start by writing 0 to dma_requested_w and dma_running_w
// then loop until 0x10 is not set here.
r |= 0x10;
}
return r;
}
void jaleco_vj_king_qtaro_device::int_w(offs_t offset, uint32_t data)
{
// 0x1000000 is used to trigger an event interrupt in the Qtaro driver.
// The interrupt will only be accepted and cleared when event_r, event2_r, event_io_r are non-zero.
// It's set, read, and cleared all in the device driver on the PC so no need to handle it here.
m_int = data;
}
uint32_t jaleco_vj_king_qtaro_device::int_fpga_r(offs_t offset)
{
return m_int_fpga;
}
void jaleco_vj_king_qtaro_device::int_fpga_w(offs_t offset, uint32_t data)
{
m_int_fpga = data;
}
template <int DeviceId>
void jaleco_vj_king_qtaro_device::dma_requested_w(offs_t offset, uint32_t data)
{
m_dma_running[DeviceId] = data == 1;
if (data == 1 && m_dma_descriptor_requested_addr[DeviceId] != 0) {
m_dma_descriptor_addr[DeviceId] = m_dma_descriptor_requested_addr[DeviceId];
m_dma_descriptor_length[DeviceId] = 0;
m_dma_descriptor_requested_addr[DeviceId] = 0;
}
LOGMASKED(LOG_DMA, "%lf %s dma_requested_w<%d>: %08x\n", machine().time().as_double(), machine().describe_context().c_str(), DeviceId, data);
}
template <int DeviceId>
void jaleco_vj_king_qtaro_device::dma_descriptor_phys_addr_w(offs_t offset, uint32_t data)
{
LOGMASKED(LOG_DMA, "dma_descriptor_phys_addr_w<%d>: %08x %d\n", DeviceId, data, m_dma_running[DeviceId]);
m_dma_descriptor_requested_addr[DeviceId] = data;
}
template <int DeviceId>
uint32_t jaleco_vj_king_qtaro_device::dma_running_r(offs_t offset)
{
return m_dma_running[DeviceId];
}
template <int DeviceId>
void jaleco_vj_king_qtaro_device::dma_running_w(offs_t offset, uint32_t data)
{
LOGMASKED(LOG_DMA, "dma_running_w<%d>: %08x\n", DeviceId, data);
if (data == 0)
m_dma_running[DeviceId] = false;
}
TIMER_CALLBACK_MEMBER(jaleco_vj_king_qtaro_device::video_dma_callback)
{
for (int device_id = 0; device_id < 3; device_id++) {
if (!m_dma_running[device_id] || BIT(m_dma_descriptor_addr[device_id], 0)) {
m_dma_running[device_id] = false;
continue;
}
const uint32_t dmaLength = m_dma_space->read_dword(m_dma_descriptor_addr[device_id] + 4);
const uint32_t bufferPhysAddr = m_dma_space->read_dword(m_dma_descriptor_addr[device_id] + 8);
const uint32_t burstLength = std::min(dmaLength - m_dma_descriptor_length[device_id], DMA_BURST_SIZE);
if (burstLength == 0)
continue;
LOGMASKED(LOG_VERBOSE_DMA, "DMA %d copy %08x + %04x = %08x: %08x bytes\n", device_id, bufferPhysAddr, m_dma_descriptor_length[device_id], bufferPhysAddr + m_dma_descriptor_length[device_id], burstLength);
uint8_t buf[DMA_BURST_SIZE];
for (int i = 0; i < burstLength; i++) {
buf[i] = m_dma_space->read_byte(bufferPhysAddr + m_dma_descriptor_length[device_id]);
m_dma_descriptor_length[device_id]++;
}
m_qtaro[device_id]->write(buf, burstLength);
if (m_dma_running[device_id] && m_dma_descriptor_length[device_id] >= dmaLength) {
const uint32_t nextDescriptorPhysAddr = m_dma_space->read_dword(m_dma_descriptor_addr[device_id]);
const uint32_t flags = m_dma_space->read_dword(m_dma_descriptor_addr[device_id] + 12); // Bit 24 is set to denote the last entry at the same time as bit 0 of the next descriptor addr is set
LOGMASKED(LOG_DMA, "DMA %d: %08x -> %08x %08x (%d %d)\n", device_id, m_dma_descriptor_addr[device_id], nextDescriptorPhysAddr, m_dma_descriptor_length[device_id], BIT(nextDescriptorPhysAddr, 0), BIT(flags, 24));
m_dma_descriptor_addr[device_id] = nextDescriptorPhysAddr;
m_dma_descriptor_length[device_id] = 0;
m_dma_running[device_id] = BIT(m_dma_descriptor_addr[device_id], 0) == 0 && BIT(flags, 24) == 0;
}
}
}
void jaleco_vj_king_qtaro_device::map(address_map &map)
{
map(0x10, 0x10).r(m_qtaro[0], FUNC(jaleco_vj_qtaro_device::reg2_r));
map(0x18, 0x1b).rw(m_qtaro[0], FUNC(jaleco_vj_qtaro_device::reg3_r), FUNC(jaleco_vj_qtaro_device::reg3_w));
map(0x20, 0x20).r(m_qtaro[1], FUNC(jaleco_vj_qtaro_device::reg2_r));
map(0x28, 0x2b).rw(m_qtaro[1], FUNC(jaleco_vj_qtaro_device::reg3_r), FUNC(jaleco_vj_qtaro_device::reg3_w));
map(0x30, 0x30).r(m_qtaro[2], FUNC(jaleco_vj_qtaro_device::reg2_r));
map(0x38, 0x3b).rw(m_qtaro[2], FUNC(jaleco_vj_qtaro_device::reg3_r), FUNC(jaleco_vj_qtaro_device::reg3_w));
map(0x50, 0x53).w(FUNC(jaleco_vj_king_qtaro_device::dma_requested_w<0>));
map(0x54, 0x57).w(FUNC(jaleco_vj_king_qtaro_device::dma_descriptor_phys_addr_w<0>));
map(0x58, 0x5b).rw(FUNC(jaleco_vj_king_qtaro_device::dma_running_r<0>), FUNC(jaleco_vj_king_qtaro_device::dma_running_w<0>));
map(0x60, 0x63).w(FUNC(jaleco_vj_king_qtaro_device::dma_requested_w<1>));
map(0x64, 0x67).w(FUNC(jaleco_vj_king_qtaro_device::dma_descriptor_phys_addr_w<1>));
map(0x68, 0x6b).rw(FUNC(jaleco_vj_king_qtaro_device::dma_running_r<1>), FUNC(jaleco_vj_king_qtaro_device::dma_running_w<1>));
map(0x70, 0x73).w(FUNC(jaleco_vj_king_qtaro_device::dma_requested_w<2>));
map(0x74, 0x77).w(FUNC(jaleco_vj_king_qtaro_device::dma_descriptor_phys_addr_w<2>));
map(0x78, 0x7b).rw(FUNC(jaleco_vj_king_qtaro_device::dma_running_r<2>), FUNC(jaleco_vj_king_qtaro_device::dma_running_w<2>));
map(0x80, 0x83).rw(FUNC(jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_status_r), FUNC(jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_status_w));
map(0x84, 0x87).rw(FUNC(jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_r), FUNC(jaleco_vj_king_qtaro_device::qtaro_fpga_firmware_w));
map(0x88, 0x8b).rw(FUNC(jaleco_vj_king_qtaro_device::fpga_firmware_status_r), FUNC(jaleco_vj_king_qtaro_device::fpga_firmware_status_w));
map(0x8c, 0x8f).rw(FUNC(jaleco_vj_king_qtaro_device::fpga_firmware_r), FUNC(jaleco_vj_king_qtaro_device::fpga_firmware_w));
map(0x90, 0x94).rw(FUNC(jaleco_vj_king_qtaro_device::event_io_r), FUNC(jaleco_vj_king_qtaro_device::event_io_w));
map(0x98, 0x9c).rw(FUNC(jaleco_vj_king_qtaro_device::event_unk_r), FUNC(jaleco_vj_king_qtaro_device::event_unk_w));
map(0xa0, 0xa4).rw(FUNC(jaleco_vj_king_qtaro_device::event_io_mask_r), FUNC(jaleco_vj_king_qtaro_device::event_io_mask_w));
map(0xa8, 0xab).rw(FUNC(jaleco_vj_king_qtaro_device::event_mask_r), FUNC(jaleco_vj_king_qtaro_device::event_mask_w));
map(0xac, 0xaf).rw(FUNC(jaleco_vj_king_qtaro_device::event_r), FUNC(jaleco_vj_king_qtaro_device::event_w));
map(0xb1, 0xb1).rw(m_qtaro[0], FUNC(jaleco_vj_qtaro_device::reg_r), FUNC(jaleco_vj_qtaro_device::reg_w));
map(0xb2, 0xb2).rw(m_qtaro[1], FUNC(jaleco_vj_qtaro_device::reg_r), FUNC(jaleco_vj_qtaro_device::reg_w));
map(0xb3, 0xb3).rw(m_qtaro[2], FUNC(jaleco_vj_qtaro_device::reg_r), FUNC(jaleco_vj_qtaro_device::reg_w));
map(0xb4, 0xb7).rw(FUNC(jaleco_vj_king_qtaro_device::int_r), FUNC(jaleco_vj_king_qtaro_device::int_w));
map(0xb8, 0xbb).rw(FUNC(jaleco_vj_king_qtaro_device::int_fpga_r), FUNC(jaleco_vj_king_qtaro_device::int_fpga_w));
}
jaleco_vj_king_qtaro_device::jaleco_vj_king_qtaro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
jaleco_vj_king_qtaro_device(mconfig, JALECO_VJ_KING_QTARO, tag, owner, clock)
{
}
jaleco_vj_king_qtaro_device::jaleco_vj_king_qtaro_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
pci_device(mconfig, type, tag, owner, clock),
m_dma_space(*this, finder_base::DUMMY_TAG, -1, 32),
m_qtaro(*this, "qtaro%u", 1)
{
}
void jaleco_vj_king_qtaro_device::device_start()
{
pci_device::device_start();
set_ids(0x11ca0007, 0x01, 0x068000, 0x00000000);
intr_pin = 1; // TODO: Verify with real hardware
intr_line = 10; // TODO: No idea what this should be on real hardware, but a valid IRQ is required to work
add_map(256, M_MEM, FUNC(jaleco_vj_king_qtaro_device::map));
m_dma_timer = timer_alloc(FUNC(jaleco_vj_king_qtaro_device::video_dma_callback), this);
m_dma_timer->adjust(DMA_TIMER_PERIOD, 0, DMA_TIMER_PERIOD);
save_item(NAME(m_int));
save_item(NAME(m_int_fpga));
save_item(NAME(m_event));
save_item(NAME(m_event_mask));
save_item(NAME(m_event_io));
save_item(NAME(m_event_io_mask));
save_item(NAME(m_event_unk));
save_item(NAME(m_event_unk_mask));
save_item(NAME(m_dma_running));
save_item(NAME(m_dma_descriptor_requested_addr));
save_item(NAME(m_dma_descriptor_addr));
save_item(NAME(m_dma_descriptor_length));
}
void jaleco_vj_king_qtaro_device::device_reset()
{
m_int = 0;
m_int_fpga = 0;
m_event = m_event_mask = 0;
std::fill(std::begin(m_event_io), std::end(m_event_io), 0);
std::fill(std::begin(m_event_io_mask), std::end(m_event_io_mask), 0);
std::fill(std::begin(m_event_unk), std::end(m_event_unk), 0);
std::fill(std::begin(m_event_unk_mask), std::end(m_event_unk_mask), 0);
std::fill(std::begin(m_dma_running), std::end(m_dma_running), false);
std::fill(std::begin(m_dma_descriptor_requested_addr), std::end(m_dma_descriptor_requested_addr), 0);
std::fill(std::begin(m_dma_descriptor_addr), std::end(m_dma_descriptor_addr), 0);
std::fill(std::begin(m_dma_descriptor_length), std::end(m_dma_descriptor_length), 0);
}
void jaleco_vj_king_qtaro_device::device_add_mconfig(machine_config &config)
{
JALECO_VJ_QTARO(config, m_qtaro[0], 0);
JALECO_VJ_QTARO(config, m_qtaro[1], 0);
JALECO_VJ_QTARO(config, m_qtaro[2], 0);
}

View File

@ -0,0 +1,133 @@
// license:BSD-3-Clause
// copyright-holders:windyfairy
#ifndef MAME_JALECO_JALECO_VJ_QTARO_H
#define MAME_JALECO_JALECO_VJ_QTARO_H
#pragma once
#include "machine/pci.h"
class jaleco_vj_qtaro_device : public device_t
{
public:
jaleco_vj_qtaro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
static constexpr feature_type imperfect_features() {
return feature::TIMING; // DMA timings aren't perfectly synced between all displays so one video stream may end up out of sync
}
void video_mix_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
uint8_t reg_r(offs_t offset);
void reg_w(offs_t offset, uint8_t data);
uint8_t reg2_r(offs_t offset);
uint32_t reg3_r(offs_t offset);
void reg3_w(offs_t offset, uint32_t data);
void write(uint8_t *data, uint32_t len);
protected:
virtual void device_start() override;
virtual void device_reset() override;
private:
uint8_t m_int;
uint32_t m_mix_level;
};
/////////////////////////////////////////////
class jaleco_vj_king_qtaro_device : public pci_device
{
public:
jaleco_vj_king_qtaro_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// FIXME: this is a workaround for the PCI frameworks lack of bus mastering DMA support
template <typename... T> void set_bus_master_space(T &&... args) { m_dma_space.set_tag(std::forward<T>(args)...); }
template <int DeviceId> void video_mix_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) { m_qtaro[DeviceId]->video_mix_w(offset, data, mem_mask); }
void video_control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
protected:
jaleco_vj_king_qtaro_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
private:
void map(address_map &map);
uint32_t qtaro_fpga_firmware_status_r(offs_t offset);
void qtaro_fpga_firmware_status_w(offs_t offset, uint32_t data);
uint32_t qtaro_fpga_firmware_r(offs_t offset);
void qtaro_fpga_firmware_w(offs_t offset, uint32_t data);
uint32_t fpga_firmware_status_r(offs_t offset);
void fpga_firmware_status_w(offs_t offset, uint32_t data);
uint32_t fpga_firmware_r(offs_t offset);
void fpga_firmware_w(offs_t offset, uint32_t data);
uint8_t event_io_mask_r(offs_t offset);
void event_io_mask_w(offs_t offset, uint8_t data);
uint8_t event_unk_r(offs_t offset);
void event_unk_w(offs_t offset, uint8_t data);
uint8_t event_io_r(offs_t offset);
void event_io_w(offs_t offset, uint8_t data);
uint32_t event_r(offs_t offset);
void event_w(offs_t offset, uint32_t data);
uint32_t event_mask_r(offs_t offset);
void event_mask_w(offs_t offset, uint32_t data);
uint32_t int_r(offs_t offset);
void int_w(offs_t offset, uint32_t data);
uint32_t int_fpga_r(offs_t offset);
void int_fpga_w(offs_t offset, uint32_t data);
template <int DeviceId> void dma_requested_w(offs_t offset, uint32_t data);
template <int DeviceId> void dma_descriptor_phys_addr_w(offs_t offset, uint32_t data);
template <int DeviceId> uint32_t dma_running_r(offs_t offset);
template <int DeviceId> void dma_running_w(offs_t offset, uint32_t data);
TIMER_CALLBACK_MEMBER(video_dma_callback);
required_address_space m_dma_space;
required_device_array<jaleco_vj_qtaro_device, 3> m_qtaro;
emu_timer* m_dma_timer;
uint32_t m_int;
uint32_t m_int_fpga;
uint32_t m_event, m_event_mask;
uint8_t m_event_io[5], m_event_io_mask[5];
uint8_t m_event_unk[5], m_event_unk_mask[5];
bool m_dma_running[3];
uint32_t m_dma_descriptor_requested_addr[3];
uint32_t m_dma_descriptor_addr[3];
uint32_t m_dma_descriptor_length[3];
};
/////////////////////////////////////////////
DECLARE_DEVICE_TYPE(JALECO_VJ_QTARO, jaleco_vj_qtaro_device)
DECLARE_DEVICE_TYPE(JALECO_VJ_KING_QTARO, jaleco_vj_king_qtaro_device)
#endif // MAME_JALECO_JALECO_VJ_QTARO_H

View File

@ -939,11 +939,10 @@ void stepstag_state::stepstag_sub_map(address_map &map)
map(0x400000, 0x43ffff).ram().w(FUNC(stepstag_state::stepstag_palette_mid_w)).share("paletteram2");
map(0x500000, 0x53ffff).ram().w(FUNC(stepstag_state::stepstag_palette_right_w)).share("paletteram3");
// rgb brightness?
map(0x700000, 0x700001).nopw(); // 0-f
map(0x700002, 0x700003).nopw(); // 0-f
map(0x700004, 0x700005).nopw(); // 0-f
map(0x700006, 0x700007).nopw(); // 0-3f (high bits?)
map(0x700000, 0x700001).w(m_jaleco_vj_pc, FUNC(jaleco_vj_pc_device::video_mix_w<0>));
map(0x700002, 0x700003).w(m_jaleco_vj_pc, FUNC(jaleco_vj_pc_device::video_mix_w<1>));
map(0x700004, 0x700005).w(m_jaleco_vj_pc, FUNC(jaleco_vj_pc_device::video_mix_w<2>));
map(0x700006, 0x700007).w(m_jaleco_vj_pc, FUNC(jaleco_vj_pc_device::video_control_w));
// left screen sprites
map(0x800000, 0x8007ff).ram().share("spriteram1"); // Object RAM