// license:BSD-3-Clause
// copyright-holders:David Haywood
/***************************************************************************
The 'VT' series are SoC solutions that implement enhanced NES hardware
there are several generations of these chips each adding additional
functionality.
This list is incomplete
VT01 - plain famiclone?
VT02 - banking scheme to access 32MB, Dual APU with PCM support
VT03 - above + 4bpp sprite / bg modes, enhanced palette
VT08 - ?
VT09 - alt 4bpp modes?
VT16 - ?
VT18 - ?
VT33 (?) - used in FC Pocket, dgun2573
Adds scrambled opcodes (XORed with 0xA1) and RGB444 palette mode,
and more advanced PCM modes (CPU and video working, sound NYI)
VT368 (?) - used in DGUN2561, lxcmcy
Various enhancements not yet emulated. Different banking, possibly an ALU,
larger palette space
VT36x (?) - used in SY889
Uses SQI rather than parallel flash
Vaguely OneBus compatible but some registers different ($411C in particular)
Uses RGB format for palettes
Credit to NewRisingSun2 for much of the reverse engineering
same chipset used in Mogis M320, but uses more advanced feature set.
(more)
todo (VT03):
APU refactoring to allow for mostly doubled up functionality + PCM channel
*more*
todo (newer VTxx):
new PCM audio in FC Pocket and DGUN-2573
add support for VT368 (?) in DGUN-2561 and lxcmcy
add support for the VT369 (?) featurs used by the MOGIS M320
**************************************************************************/
#include "emu.h"
#include "nes_vt_soc.h"
// TODO: identify what kind of SoCs each of these are (some are probably meant to be the same chip, just with subsets of the features added at the moment, especially the CY/BT/HH ones)
// also work out if some of these features (eg. opcode scrambling) should be done with external callbacks, sometimes the die was the same (VH2009) but encryption not always present (pin control or external feature?)
DEFINE_DEVICE_TYPE(NES_VT_SOC, nes_vt_soc_device, "nes_vt_soc", "VTxx series System on a Chip (NTSC)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_PAL, nes_vt_soc_pal_device, "nes_vt_soc_pal", "VTxx series System on a Chip (PAL)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_SCRAMBLE, nes_vt_soc_scramble_device, "nes_vt_soc_scram", "VTxx series System on a Chip (with simple Opcode scrambling)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_4KRAM, nes_vt_soc_4kram_device, "nes_vt_soc_4k", "VTxx series System on a Chip (with 4KByte RAM)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_4KRAM_CY, nes_vt_soc_4kram_cy_device, "nes_vt_soc_4k_cy", "VTxx series System on a Chip (with 4KByte RAM) (CY)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_4KRAM_BT, nes_vt_soc_4kram_bt_device, "nes_vt_soc_4k_bt", "VTxx series System on a Chip (with 4KByte RAM) (BT)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_4KRAM_HH, nes_vt_soc_4kram_hh_device, "nes_vt_soc_4k_hh", "VTxx series System on a Chip (with 4KByte RAM) (HH)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_4KRAM_FP, nes_vt_soc_4kram_fp_device, "nes_vt_soc_4k_fp", "VTxx series System on a Chip (with 4KByte RAM) (FP) (NTSC)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_4KRAM_FP_PAL, nes_vt_soc_4kram_fp_pal_device, "nes_vt_soc_4k_fp_pal", "VTxx series System on a Chip (with 4KByte RAM) (FP) (PAL)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_8KRAM_DG, nes_vt_soc_8kram_dg_device, "nes_vt_soc_8k_dg", "VTxx series System on a Chip (with 8KByte RAM) (DG)")
DEFINE_DEVICE_TYPE(NES_VT_SOC_8KRAM_FA, nes_vt_soc_8kram_fa_device, "nes_vt_soc_8k_fa", "VTxx series System on a Chip (with 8KByte RAM) (FA)")
void nes_vt_soc_device::program_map(address_map &map)
{
}
nes_vt_soc_device::nes_vt_soc_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
device_memory_interface(mconfig, *this),
m_maincpu(*this, "maincpu"),
m_screen(*this, "screen"),
m_ppu(*this, "ppu"),
m_apu(*this, "apu"),
m_initial_e000_bank(0xff),
m_ntram(nullptr),
m_chrram(nullptr),
m_space_config("program", ENDIANNESS_LITTLE, 8, 25, 0, address_map_constructor(FUNC(nes_vt_soc_device::program_map), this)),
m_write_0_callback(*this),
m_read_0_callback(*this),
m_read_1_callback(*this),
m_extra_write_0_callback(*this),
m_extra_write_1_callback(*this),
m_extra_write_2_callback(*this),
m_extra_write_3_callback(*this),
m_extra_read_0_callback(*this),
m_extra_read_1_callback(*this),
m_extra_read_2_callback(*this),
m_extra_read_3_callback(*this)
{
// 'no scramble' configuration
for (int i = 0; i < 6; i++)
m_2012_2017_descramble[i] = 2 + i;
// 'no scramble' configuration
m_8000_scramble[0x0] = 0x6;
m_8000_scramble[0x1] = 0x7;
m_8000_scramble[0x2] = 0x2;
m_8000_scramble[0x3] = 0x3;
m_8000_scramble[0x4] = 0x4;
m_8000_scramble[0x5] = 0x5;
m_8000_scramble[0x6] = 0x7;
m_8000_scramble[0x7] = 0x8;
// 'no scramble' configuration
m_410x_scramble[0x0] = 0x7;
m_410x_scramble[0x1] = 0x8;
m_default_palette_mode = PAL_MODE_VT0x;
m_force_baddma = false;
m_use_raster_timing_hack = false;
}
nes_vt_soc_device::nes_vt_soc_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_device(mconfig, NES_VT_SOC, tag, owner, clock)
{
}
nes_vt_soc_pal_device::nes_vt_soc_pal_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_device(mconfig, NES_VT_SOC_PAL, tag, owner, clock)
{
}
nes_vt_soc_scramble_device::nes_vt_soc_scramble_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_device(mconfig, NES_VT_SOC_SCRAMBLE, tag, owner, clock)
{
}
nes_vt_soc_4kram_device::nes_vt_soc_4kram_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_device(mconfig, NES_VT_SOC_4KRAM, tag, owner, clock)
{
}
nes_vt_soc_4kram_device::nes_vt_soc_4kram_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_device(mconfig, type, tag, owner, clock),
m_upper_write_412c_callback(*this),
m_upper_read_412c_callback(*this),
m_upper_read_412d_callback(*this)
{
}
nes_vt_soc_4kram_cy_device::nes_vt_soc_4kram_cy_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_device(mconfig, NES_VT_SOC_4KRAM_CY, tag, owner, clock)
{
}
nes_vt_soc_4kram_bt_device::nes_vt_soc_4kram_bt_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_device(mconfig, NES_VT_SOC_4KRAM_BT, tag, owner, clock)
{
}
nes_vt_soc_4kram_hh_device::nes_vt_soc_4kram_hh_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_device(mconfig, type, tag, owner, clock)
{
}
nes_vt_soc_4kram_hh_device::nes_vt_soc_4kram_hh_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_hh_device(mconfig, NES_VT_SOC_4KRAM_HH, tag, owner, clock)
{
}
nes_vt_soc_4kram_fp_device::nes_vt_soc_4kram_fp_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_fp_device(mconfig, NES_VT_SOC_4KRAM_FP, tag, owner, clock)
{
}
nes_vt_soc_4kram_fp_device::nes_vt_soc_4kram_fp_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_hh_device(mconfig, type, tag, owner, clock)
{
}
nes_vt_soc_4kram_fp_pal_device::nes_vt_soc_4kram_fp_pal_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_fp_device(mconfig, NES_VT_SOC_4KRAM_FP_PAL, tag, owner, clock)
{
}
nes_vt_soc_8kram_dg_device::nes_vt_soc_8kram_dg_device(const machine_config& mconfig, device_type type, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_4kram_device(mconfig, type, tag, owner, clock)
{
}
nes_vt_soc_8kram_dg_device::nes_vt_soc_8kram_dg_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_8kram_dg_device(mconfig, NES_VT_SOC_8KRAM_DG, tag, owner, clock)
{
}
nes_vt_soc_8kram_fa_device::nes_vt_soc_8kram_fa_device(const machine_config& mconfig, const char* tag, device_t* owner, uint32_t clock) :
nes_vt_soc_8kram_dg_device(mconfig, NES_VT_SOC_8KRAM_FA, tag, owner, clock)
{
}
void nes_vt_soc_device::device_start()
{
save_item(NAME(m_410x));
save_item(NAME(m_411c));
save_item(NAME(m_411d));
save_item(NAME(m_4242));
save_item(NAME(m_8000_addr_latch));
save_item(NAME(m_timer_irq_enabled));
save_item(NAME(m_timer_running));
save_item(NAME(m_timer_val));
save_item(NAME(m_vdma_ctrl));
m_ntram = std::make_unique<uint8_t[]>(0x2000);
save_pointer(NAME(m_ntram), 0x2000);
m_chrram = std::make_unique<uint8_t[]>(0x2000);
save_pointer(NAME(m_chrram), 0x2000);
m_ppu->set_scanline_callback(*this, FUNC(nes_vt_soc_device::scanline_irq));
m_ppu->set_hblank_callback(*this, FUNC(nes_vt_soc_device::hblank_irq));
//m_ppu->set_hblank_callback(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::hblank_irq)));
//m_ppu->space(AS_PROGRAM).install_readwrite_handler(0, 0x1fff, read8sm_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::chr_r)), write8sm_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::chr_w)));
m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x2000, 0x3eff, read8sm_delegate(*this, FUNC(nes_vt_soc_device::nt_r)), write8sm_delegate(*this, FUNC(nes_vt_soc_device::nt_w)));
m_ppu->space(AS_PROGRAM).install_readwrite_handler(0, 0x1fff, read8sm_delegate(*this, FUNC(nes_vt_soc_device::chr_r)), write8sm_delegate(*this, FUNC(nes_vt_soc_device::chr_w)));
m_write_0_callback.resolve_safe();
m_read_0_callback.resolve_safe(0xff);
m_read_1_callback.resolve_safe(0xff);
m_extra_write_0_callback.resolve_safe();
m_extra_write_1_callback.resolve_safe();
m_extra_write_2_callback.resolve_safe();
m_extra_write_3_callback.resolve_safe();
m_extra_read_0_callback.resolve_safe(0xff);
m_extra_read_1_callback.resolve_safe(0xff);
m_extra_read_2_callback.resolve_safe(0xff);
m_extra_read_3_callback.resolve_safe(0xff);
}
void nes_vt_soc_device::device_reset()
{
// what are the actual defaults?
m_410x[0x0] = 0x00;
m_410x[0x1] = 0x00;
m_410x[0x2] = 0x00;
m_410x[0x3] = 0x00;
m_410x[0x4] = 0x00;
m_410x[0x5] = 0x00;
m_410x[0x6] = 0x00;
m_410x[0x7] = 0x00;
m_410x[0x8] = 0x01;
m_410x[0x9] = 0x02;
m_410x[0xa] = 0x00;
m_410x[0xb] = 0x00;
m_411c = 0x00;
m_411d = 0x00;
m_4242 = 0x00;
m_timer_irq_enabled = 0;
m_timer_running = 0;
m_timer_val = 0;
m_vdma_ctrl = 0;
update_banks();
m_ppu->set_201x_descramble(m_2012_2017_descramble[0], m_2012_2017_descramble[1], m_2012_2017_descramble[2], m_2012_2017_descramble[3], m_2012_2017_descramble[4], m_2012_2017_descramble[5]);
m_ppu->set_palette_mode(m_default_palette_mode);
}
uint32_t nes_vt_soc_device::get_banks(uint8_t bnk)
{
switch (m_410x[0xb] & 0x07)
{
case 0: return ((m_410x[0x0] & 0xF0) << 4) + ((m_410x[0xa] & 0xC0) | (bnk & 0x3F)); // makes bank 0xff at 0xe000 map to 0x07e000 by default for vectors at 0x007fffx
case 1: return ((m_410x[0x0] & 0xF0) << 4) + ((m_410x[0xa] & 0xE0) | (bnk & 0x1F));
case 2: return ((m_410x[0x0] & 0xF0) << 4) + ((m_410x[0xa] & 0xF0) | (bnk & 0x0F));
case 3: return ((m_410x[0x0] & 0xF0) << 4) + ((m_410x[0xa] & 0xF8) | (bnk & 0x07));
case 4: return ((m_410x[0x0] & 0xF0) << 4) + ((m_410x[0xa] & 0xFC) | (bnk & 0x03));
case 5: return ((m_410x[0x0] & 0xF0) << 4) + ((m_410x[0xa] & 0xFE) | (bnk & 0x01));
case 6: return ((m_410x[0x0] & 0xF0) << 4) + (m_410x[0xa]);
case 7: return ((m_410x[0x0] & 0xF0) << 4) + bnk;
}
return 0;
}
// 8000 needs to bank in 60000 ( bank 0x30 )
void nes_vt_soc_device::update_banks()
{
uint8_t bank;
// 8000-9fff
if ((m_410x[0xb] & 0x40) != 0 || (m_410x[0x5] & 0x40) == 0)
{
if ((m_410x[0x5] & 0x40) == 0)
bank = m_410x[0x7];
else
bank = m_410x[0x9];
}
else
bank = 0xfe;
m_bankaddr[0] = get_banks(bank);
// a000-bfff
bank = m_410x[0x8];
m_bankaddr[1] = get_banks(bank);
// c000-dfff
if ((m_410x[0xb] & 0x40) != 0 || (m_410x[0x5] & 0x40) != 0)
{
if ((m_410x[0x5] & 0x40) == 0)
bank = m_410x[0x9];
else
bank = m_410x[0x7];
}
else
bank = 0xfe;
m_bankaddr[2] = get_banks(bank);
// e000 - ffff
bank = m_initial_e000_bank;
m_bankaddr[3] = get_banks(bank);
}
uint16_t nes_vt_soc_device::decode_nt_addr(uint16_t addr)
{
bool vert_mirror = !(m_410x[0x6] & 0x01);
int a11 = (addr >> 11) & 0x01;
int a10 = (addr >> 10) & 0x01;
uint16_t base = (addr & 0x3FF);
return ((vert_mirror ? a10 : a11) << 10) | base;
}
void nes_vt_soc_device::vt03_410x_w(offs_t offset, uint8_t data)
{
scrambled_410x_w(offset, data);
}
uint8_t nes_vt_soc_device::vt03_410x_r(offs_t offset)
{
return m_410x[offset];
}
// Source: https://wiki.nesdev.com/w/index.php/NES_2.0_submappers/Proposals#NES_2.0_Mapper_256
void nes_vt_soc_device::scrambled_410x_w(uint16_t offset, uint8_t data)
{
switch (offset)
{
case 0x0:
m_410x[0x0] = data;
update_banks();
break;
case 0x1:
// latch timer value
m_410x[0x1] = data;
m_timer_running = 0;
break;
case 0x2:
//logerror("vt03_4102_w %02x\n", data);
// load latched value and start counting
m_410x[0x2] = data; // value doesn't matter?
m_timer_val = m_410x[0x1];
// HACK for some one line errors in various games and completely broken rasters in msifrog, TOOD: find real source of issue (bad timing of interrupt or counter changes, or latching of data?)
if (m_use_raster_timing_hack)
if (m_ppu->in_vblanking())
m_timer_val--;
m_timer_running = 1;
break;
case 0x3:
//logerror("vt03_4103_w %02x\n", data);
m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
// disable timer irq
m_410x[0x3] = data; // value doesn't matter?
m_timer_irq_enabled = 0;
break;
case 0x4:
//logerror("vt03_4104_w %02x\n", data);
// enable timer irq
m_410x[0x4] = data; // value doesn't matter?
m_timer_irq_enabled = 1;
break;
case 0x5:
logerror("vt03_4105_w %02x\n", data);
m_410x[0x5] = data;
update_banks();
break;
case 0x6:
m_410x[0x6] = data;
break;
case 0x7:
m_410x[m_410x_scramble[0]] = data;
update_banks();
break;
case 0x8:
m_410x[m_410x_scramble[1]] = data;
update_banks();
break;
case 0x9:
logerror("vt03_4109_w %02x\n", data);
m_410x[0x9] = data;
update_banks();
break;
case 0xa:
logerror("vt03_410aw %02x\n", data);
m_410x[0xa] = data;
update_banks();
break;
case 0xb:
/*
D7 TSYNEN - Timer clock select 0:AD12, 1:HSYNC
D6 Prg Bank 0 Reg 2 enable / disable 0:Disable 1:Enable
D5 RS232 enable / disable 0:Disable 1:Enable
D4 Bus output control 0: normal 1: tristate
D3 6000-7fff and 8000-ffff control - 0 will not active XRWB, 1 will activate
D2-D0 - program bank 0 selector
*/
logerror("vt03_410b_w %02x\n", data);
m_410x[0xb] = data;
update_banks();
break;
}
}
uint8_t nes_vt_soc_device::spr_r(offs_t offset)
{
if (m_4242 & 0x1 || m_411d & 0x04)
{
return m_chrram[offset];
}
else
{
int realaddr = calculate_real_video_address(offset, 0, 1);
address_space& spc = this->space(AS_PROGRAM);
return spc.read_byte(realaddr);
}
}
uint8_t nes_vt_soc_device::chr_r(offs_t offset)
{
if (m_4242 & 0x1 || m_411d & 0x04) // newer VT platforms only (not VT03/09), split out
{
return m_chrram[offset];
}
else
{
int realaddr = calculate_real_video_address(offset, 1, 0);
address_space& spc = this->space(AS_PROGRAM);
return spc.read_byte(realaddr);
}
}
void nes_vt_soc_device::chr_w(offs_t offset, uint8_t data)
{
if (m_4242 & 0x1 || m_411d & 0x04) // newer VT platforms only (not VT03/09), split out
{
logerror("vram write %04x %02x\n", offset, data);
m_chrram[offset] = data;
}
else
{
int realaddr = calculate_real_video_address(offset, 1, 0);
address_space& spc = this->space(AS_PROGRAM);
return spc.write_byte(realaddr, data);
}
}
void nes_vt_soc_device::scanline_irq(int scanline, int vblank, int blanked)
{
video_irq(false, scanline, vblank, blanked);
}
void nes_vt_soc_device::hblank_irq(int scanline, int vblank, int blanked)
{
video_irq(true, scanline, vblank, blanked);
}
void nes_vt_soc_device::video_irq(bool hblank, int scanline, int vblank, int blanked)
{
//TSYNEN
if (((m_410x[0xb] >> 7) & 0x01) == hblank)
{
int irqstate = 0;
//logerror("scanline_irq %d\n", scanline);
if (m_timer_running && scanline < 0xe0)
{
m_timer_val--;
if (m_timer_val < 0)
{
if (m_timer_irq_enabled && !blanked)
{
logerror("scanline_irq %d\n", scanline);
irqstate = 1;
}
}
}
if (irqstate)
m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
//else
// m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
}
}
/* todo, handle custom VT nametable stuff here */
uint8_t nes_vt_soc_device::nt_r(offs_t offset)
{
return m_ntram[decode_nt_addr(offset)];
}
void nes_vt_soc_device::nt_w(offs_t offset, uint8_t data)
{
//logerror("nt wr %04x %02x", offset, data);
m_ntram[decode_nt_addr(offset)] = data;
}
int nes_vt_soc_device::calculate_real_video_address(int addr, int extended, int readtype)
{
// might be a VT09 only feature (alt 4bpp mode?)
int alt_order = m_ppu->get_201x_reg(0x0) & 0x40;
if (readtype == 0)
{
if (m_ppu->get_201x_reg(0x0) & 0x10)
{
extended = 1;
}
else
{
extended = 0;
}
}
else if (readtype == 1)
{
if (m_ppu->get_201x_reg(0x0) & 0x08)
{
extended = 1;
}
else
{
extended = 0;
}
}
/*
Calculating TVA17 - TVA10
--------------------------------------------------------------------------------------------
| COMR7 | AD[12:10] | TVA17 | TVA16 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| (4105, 0x80) | (2006) | | | | | | | | |
--------------------------------------------------------------------------------------------
| 0/1/C/D | RV47 | RV46 | RV45 | RV44 | RV43 | RV42 | RV41 | AD10 | ** RV40 is never used
| 2/3/E/F | RV57 | RV56 | RV55 | RV54 | RV53 | RV52 | RV51 | AD10 | ** RV50 is never used
| 4/8 | RV07 | RV06 | RV05 | RV04 | RV03 | RV02 | RV01 | RV00 |
| 5/9 | RV17 | RV16 | RV15 | RV14 | RV13 | RV12 | RV11 | RV10 |
| 6/A | RV27 | RV26 | RV25 | RV24 | RV23 | RV22 | RV21 | RV20 |
| 7/B | RV37 | RV36 | RV35 | RV34 | RV33 | RV32 | RV31 | RV30 |
--------------------------------------------------------------------------------------------
m_r2012 = rv0x
m_r2013 = rv1x
m_r2014 = rv2x
m_r2015 = rv3x
m_r2016 = rv4x
m_r2017 = rv5x
*/
int finaladdr = 0;
int sel = (addr & 0x1c00) | ((m_410x[0x5] & 0x80) ? 0x2000 : 0x000);
int vbank_tva17_tva10 = 0x00;
switch ((sel >> 10) & 0xf)
{
case 0x0:
case 0x1:
case 0xc:
case 0xd:
vbank_tva17_tva10 = (m_ppu->get_201x_reg(0x6) & 0xfe) | ((addr & 0x0400) ? 1 : 0);
break;
case 0x2:
case 0x3:
case 0xe:
case 0xf:
vbank_tva17_tva10 = (m_ppu->get_201x_reg(0x7) & 0xfe) | ((addr & 0x0400) ? 1 : 0);
break;
case 0x4:
case 0x8:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x2);
break;
case 0x5:
case 0x9:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x3);
break;
case 0x6:
case 0xa:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x4);
break;
case 0x7:
case 0xb:
vbank_tva17_tva10 = m_ppu->get_201x_reg(0x5);
break;
}
/*
Calculating VA17 - VA10 (requires TVA17-TVA10 to have been calculated)
------------------------------------------------------------------------------
| VB0S[2:0] | VA[17:10] |
| 201a & 0x7 | VA17 | VA16 | VA15 | VA14 | VA13 | VA12 | VA11 | VA10 |
|-----------------------------------------------------------------------------
| 0x0 | TVA17 | TVA16 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x1 | TV67 | TVA16 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x2 | RV67 | RV66 | TVA15 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x3 | INVALID ***************************************************** |
| 0x4 | RV67 | RV66 | RV65 | TVA14 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x5 | RV67 | RV66 | RV65 | RV64 | TVA13 | TVA12 | TVA11 | TVA10 |
| 0x6 | RV67 | RV66 | RV65 | RV64 | RV63 | TVA12 | TVA11 | TVA10 |
| 0x7 | INVALID ***************************************************** |
------------------------------------------------------------------------------
RV67- RV63 = 0x201a & 0xf8
*/
int va17_va10 = 0;
int swit = m_ppu->get_201x_reg(0xa);
switch (swit & 0x07)
{
case 0x0: va17_va10 = vbank_tva17_tva10; break;
case 0x1: va17_va10 = (vbank_tva17_tva10 & 0x7f) | (m_ppu->get_201x_reg(0xa) & 0x80); break;
case 0x2: va17_va10 = (vbank_tva17_tva10 & 0x3f) | (m_ppu->get_201x_reg(0xa) & 0xc0); break;
case 0x3: return -1;
case 0x4: va17_va10 = (vbank_tva17_tva10 & 0x1f) | (m_ppu->get_201x_reg(0xa) & 0xe0); break;
case 0x5: va17_va10 = (vbank_tva17_tva10 & 0x0f) | (m_ppu->get_201x_reg(0xa) & 0xf0); break;
case 0x6: va17_va10 = (vbank_tva17_tva10 & 0x07) | (m_ppu->get_201x_reg(0xa) & 0xf8); break;
case 0x7: return -1;
}
int va34 = m_ppu->get_va34();
if (!extended)
{
int is4bpp = 0;
if (readtype == 0) is4bpp = m_ppu->get_201x_reg(0x0) & 0x02;
else if (readtype == 1) is4bpp = m_ppu->get_201x_reg(0x0) & 0x04;
int va20_va18 = (m_ppu->get_201x_reg(0x8) & 0x70) >> 4;
finaladdr = ((m_410x[0x0] & 0x0F) << 21) | (va20_va18 << 18) | (va17_va10 << 10) | (addr & 0x03ff);
if (is4bpp)
{
if (!alt_order)
{
finaladdr = ((finaladdr & ~0xf) << 1) | (va34 << 4) | (finaladdr & 0xf);
}
else
{
finaladdr = (finaladdr << 1) | va34;
}
}
}
else
{
int eva2_eva0 = 0x00;
int is4bpp = 0;
switch (readtype)
{
case 0: // background display
is4bpp = m_ppu->get_201x_reg(0x0) & 0x02;
eva2_eva0 |= m_ppu->get_m_read_bg4_bg3();
if (m_ppu->get_201x_reg(0x1) & 0x02)
{
if (m_410x[0x6] & 0x1) eva2_eva0 |= 0x4;
}
else
{
if (m_ppu->get_201x_reg(0x8) & 0x08) eva2_eva0 |= 0x4;
}
break;
case 1: // sprite display
is4bpp = m_ppu->get_201x_reg(0x0) & 0x04; // 16 colors or 16-pixel wide (both adjust the read)
eva2_eva0 |= m_ppu->get_speva2_speva0();
break;
case 2: // CPU R/W access
// todo
break;
}
finaladdr = ((m_410x[0x0] & 0x0f) << 21) | (va17_va10 << 13) | (eva2_eva0 << 10) | (addr & 0x03ff);
if (is4bpp)
{
if (!alt_order)
{
finaladdr = ((finaladdr & ~0xf) << 1) | (va34 << 4) | (finaladdr & 0xf);
}
else
{
finaladdr = (finaladdr << 1) | va34;
}
}
}
return finaladdr;
}
/*
nes_vt_soc_device::vt03_8000_mapper_w notes
used for MMC3/other mapper compatibility
some consoles have scrambled registers for crude copy protection
*/
void nes_vt_soc_device::scrambled_8000_w(uint16_t offset, uint8_t data)
{
offset &= 0x7fff;
uint16_t addr = offset+0x8000;
if ((m_411d & 0x01) && (m_411d & 0x03)) // this condition is nonsense, maybe should be ((m_411d & 0x03) == 0x03) check it! (newer VT only, not VT03/09, split)
{
//CNROM compat
logerror("%s: vtxx_cnrom_8000_w real address: (%04x) translated address: (%04x) %02x\n", machine().describe_context(), addr, offset + 0x8000, data);
m_ppu->set_201x_reg(0x6, data * 8);
m_ppu->set_201x_reg(0x7, data * 8 + 2);
m_ppu->set_201x_reg(0x2, data * 8 + 4);
m_ppu->set_201x_reg(0x3, data * 8 + 5);
m_ppu->set_201x_reg(0x4, data * 8 + 6);
m_ppu->set_201x_reg(0x5, data * 8 + 7);
}
else if (m_411d & 0x01) // (newer VT only, not VT03/09, split)
{
//MMC1 compat, TODO
logerror("%s: vtxx_mmc1_8000_w real address: (%04x) translated address: (%04x) %02x\n", machine().describe_context(), addr, offset + 0x8000, data);
}
else if (m_411d & 0x02) // (newer VT only, not VT03/09, split)
{
//UNROM compat
logerror("%s: vtxx_unrom_8000_w real address: (%04x) translated address: (%04x) %02x\n", machine().describe_context(), addr, offset + 0x8000, data);
m_410x[0x7] = ((data & 0x0F) << 1);
m_410x[0x8] = ((data & 0x0F) << 1) + 1;
update_banks();
}
else // standard mode (VT03/09)
{
//logerror("%s: vtxx_mmc3_8000_w real address: (%04x) translated address: (%04x) %02x\n", machine().describe_context(), addr, offset+0x8000, data );
//MMC3 compat
if ((addr < 0xA000) && !(addr & 0x01))
{
logerror("%s: scrambled_8000_w real address: (%04x) translated address: (%04x) %02x (banking)\n", machine().describe_context(), addr, offset + 0x8000, data);
// Bank select
m_8000_addr_latch = data & 0x07;
// Bank config
m_410x[0x05] = data & ~(1 << 5);
update_banks();
}
else if ((addr < 0xA000) && (addr & 0x01))
{
logerror("%s: scrambled_8000_w real address: (%04x) translated address: (%04x) %02x (other scrambled stuff)\n", machine().describe_context(), addr, offset + 0x8000, data);
switch (m_410x[0x05] & 0x07)
{
case 0x00:
m_ppu->set_201x_reg(m_8000_scramble[0], data);
break;
case 0x01:
m_ppu->set_201x_reg(m_8000_scramble[1], data);
break;
case 0x02: // hand?
m_ppu->set_201x_reg(m_8000_scramble[2], data);
break;
case 0x03: // dog?
m_ppu->set_201x_reg(m_8000_scramble[3], data);
break;
case 0x04: // ball thrown
m_ppu->set_201x_reg(m_8000_scramble[4], data);
break;
case 0x05: // ball thrown
m_ppu->set_201x_reg(m_8000_scramble[5], data);
break;
case 0x06:
m_410x[m_8000_scramble[6]] = data;
//m_410x[0x9] = data;
update_banks();
break;
case 0x07:
m_410x[m_8000_scramble[7]] = data;
update_banks();
break;
}
}
else if ((addr >= 0xA000) && (addr < 0xC000) && !(addr & 0x01))
{
// Mirroring
m_410x[0x6] &= 0xFE;
m_410x[0x6] |= data & 0x01;
}
else if ((addr >= 0xA000) && (addr < 0xC000) && (addr & 0x01))
{
// PRG RAM control, ignore
}
else if ((addr >= 0xC000) && (addr < 0xE000) && !(addr & 0x01))
{
// IRQ latch
vt03_410x_w(1, data);
}
else if ((addr >= 0xC000) && (addr < 0xE000) && (addr & 0x01))
{
// IRQ reload
vt03_410x_w(2, data);
}
else if ((addr >= 0xE000) && !(addr & 0x01))
{
// IRQ disable
vt03_410x_w(3, data);
}
else if ((addr >= 0xE000) && (addr & 0x01))
{
// IRQ enable
vt03_410x_w(4, data);
}
else
{
}
}
}
// MMC3 compatibility mode
void nes_vt_soc_device::set_8000_scramble(uint8_t reg0, uint8_t reg1, uint8_t reg2, uint8_t reg3, uint8_t reg4, uint8_t reg5, uint8_t reg6, uint8_t reg7)
{
m_8000_scramble[0] = reg0; // TODO: name the regs
m_8000_scramble[1] = reg1;
m_8000_scramble[2] = reg2;
m_8000_scramble[3] = reg3;
m_8000_scramble[4] = reg4;
m_8000_scramble[5] = reg5;
m_8000_scramble[6] = reg6;
m_8000_scramble[7] = reg7;
}
void nes_vt_soc_device::set_410x_scramble(uint8_t reg0, uint8_t reg1)
{
m_410x_scramble[0] = reg0; // TODO: name the regs
m_410x_scramble[1] = reg1;
}
void nes_vt_soc_device::vt03_8000_mapper_w(offs_t offset, uint8_t data)
{
scrambled_8000_w(offset, data);
//logerror("%s: vt03_8000_mapper_w (%04x) %02x\n", machine().describe_context(), offset+0x8000, data );
}
/* APU plumbing, this is because we have a plain M6502 core in the VT03, otherwise this is handled in the core */
uint8_t nes_vt_soc_device::psg1_4014_r()
{
//return m_apu->read(0x14);
return 0x00;
}
uint8_t nes_vt_soc_device::psg1_4015_r()
{
return m_apu->read(0x15);
}
void nes_vt_soc_device::psg1_4015_w(uint8_t data)
{
m_apu->write(0x15, data);
}
void nes_vt_soc_device::psg1_4017_w(uint8_t data)
{
m_apu->write(0x17, data);
}
// early units (VT03?) have a DMA bug in NTSC mode
void nes_vt_soc_device::vt_dma_w(uint8_t data)
{
if (!m_force_baddma)
do_dma(data, true);
else
do_dma(data, false);
}
void nes_vt_soc_device::do_dma(uint8_t data, bool has_ntsc_bug)
{
// only NTSC systems have 'broken' DMA which requires the DMA addresses to be shifted by 1, PAL systems work as expected
if (m_ppu->get_is_pal())
has_ntsc_bug = false;
uint8_t dma_mode = m_vdma_ctrl & 0x01;
uint8_t dma_len = (m_vdma_ctrl >> 1) & 0x07;
uint8_t src_nib_74 = (m_vdma_ctrl >> 4) & 0x0F;
int length = 256;
switch (dma_len)
{
case 0x0: length = 256; break;
case 0x4: length = 16; break;
case 0x5: length = 32; break;
case 0x6: length = 64; break;
case 0x7: length = 128; break;
}
uint16_t src_addr = (data << 8) | (src_nib_74 << 4);
logerror("vthh dma start ctrl=%02x addr=%04x\n", m_vdma_ctrl, src_addr);
if (dma_mode == 1)
{
logerror("vdma dest %04x\n", m_ppu->get_vram_dest());
}
if (has_ntsc_bug && (dma_mode == 1) && ((m_ppu->get_vram_dest() & 0xFF00) == 0x3F00) && !(m_ppu->get_201x_reg(0x1) & 0x80))
{
length -= 1;
src_addr += 1;
}
for (int i = 0; i < length; i++)
{
uint8_t spriteData = m_maincpu->space(AS_PROGRAM).read_byte(src_addr + i);
if (dma_mode)
{
m_maincpu->space(AS_PROGRAM).write_byte(0x2007, spriteData);
}
else
{
m_maincpu->space(AS_PROGRAM).write_byte(0x2004, spriteData);
}
//if(((src_addr + i) & 0xFF) == length && (i != 0)) break;
}
// should last (length * 4 - 1) CPU cycles.
//((device_t*)m_maincpu)->execute().adjust_icount(-(length * 4 - 1));
}
void nes_vt_soc_device::vt03_4034_w(uint8_t data)
{
logerror("vt03_4034_w %02x\n", data);
m_vdma_ctrl = data;
}
uint8_t nes_vt_soc_device::in0_r()
{
return m_read_0_callback();
}
uint8_t nes_vt_soc_device::in1_r()
{
return m_read_1_callback();
}
void nes_vt_soc_device::in0_w(offs_t offset, uint8_t data)
{
m_write_0_callback(offset, data);
}
void nes_vt_soc_device::extra_io_control_w(uint8_t data)
{
/*
410d Extra I/O control
0x01 Extra I/O port 0 mode (1 = output, 0 = input)
0x02 Extra I/O port 0 enable (1 = enable, 0 = disable)
0x04 Extra I/O port 1 mode (1 = output, 0 = input)
0x08 Extra I/O port 1 enable (1 = enable, 0 = disable)
0x10 Extra I/O port 2 mode (1 = output, 0 = input)
0x20 Extra I/O port 2 enable (1 = enable, 0 = disable)
0x40 Extra I/O port 3 mode (1 = output, 0 = input)
0x80 Extra I/O port 3 enable (1 = enable, 0 = disable)
*/
logerror("%s: extra_io_control_w %02x\n", machine().describe_context(), data);
}
uint8_t nes_vt_soc_device::extrain_01_r()
{
// TODO: check status of 410d port to make sure we only read from enabled ports
uint8_t in0 = 0x00, in1 = 0x00;
in0 = m_extra_read_0_callback() & 0x0f;
in1 = m_extra_read_1_callback() & 0x0f;
return in0 | (in1<<4);
}
uint8_t nes_vt_soc_device::extrain_23_r()
{
// TODO: check status of 410d port to make sure we only read from enabled ports
uint8_t in2 = 0x00, in3 = 0x00;
in2 = m_extra_read_2_callback() & 0x0f;
in3 = m_extra_read_3_callback() & 0x0f;
return in2 | (in3<<4);
}
void nes_vt_soc_device::extraout_01_w(uint8_t data)
{
// TODO: use callbacks for this as output can be hooked up to anything
logerror("%s: extraout_01_w %02x\n", machine().describe_context(), data);
}
void nes_vt_soc_device::extraout_23_w(uint8_t data)
{
// TODO: use callbacks for this as output can be hooked up to anything
logerror("%s: extraout_23_w %02x\n", machine().describe_context(), data);
}
uint8_t nes_vt_soc_device::rs232flags_region_r()
{
/*
0x4119 RS232 Flags + Region
0x01 - RX bit 8
0x02 - RERFF (error status)
0x04 - unused
0x08 - XPORN (PAL = 1 NTSC = 0)
0x10 - XF5OR6 (50hz = 1 60hz = 0)
0x20 - RINGF (receive status)
0x40 - TIFLAG (completed sending data status)
0x80 - RIFLAG (completed receiving data status)
*/
uint8_t ret = 0x00;
// Palette DMA is buggy on NTSC systems (at least for regular VT03)
// so the palette DMA writes will change based on the reading of these flags
ret |= m_ppu->get_is_pal() ? 0x08 : 0x00;
ret |= m_ppu->get_is_50hz() ? 0x10 : 0x00;
return ret;
}
uint8_t nes_vt_soc_device::external_space_read(offs_t offset)
{
address_space& spc = this->space(AS_PROGRAM);
int bank = (offset & 0x6000) >> 13;
int address = (m_bankaddr[bank] * 0x2000) + (offset & 0x1fff);
return spc.read_byte(address);
}
void nes_vt_soc_device::external_space_write(offs_t offset, uint8_t data)
{
if ((m_410x[0xb] & 0x08))
{
address_space& spc = this->space(AS_PROGRAM);
int bank = (offset & 0x6000) >> 13;
int address = (m_bankaddr[bank] * 0x2000) + (offset & 0x1fff);
spc.write_byte(address, data);
}
else
{
vt03_8000_mapper_w(offset, data);
}
};
void nes_vt_soc_device::nes_vt_map(address_map &map)
{
map(0x0000, 0x07ff).ram().mirror(0x1800); // zudugo relies on mirror when selecting 'game' menu
// ddrdismx relies on the mirroring
map(0x2000, 0x2007).mirror(0x00e0).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write)); // standard PPU registers
map(0x2010, 0x201f).mirror(0x00e0).rw(m_ppu, FUNC(ppu_vt03_device::read_extended), FUNC(ppu_vt03_device::write_extended)); // extra VT PPU registers
map(0x4000, 0x4013).rw(m_apu, FUNC(nesapu_device::read), FUNC(nesapu_device::write));
map(0x4014, 0x4014).r(FUNC(nes_vt_soc_device::psg1_4014_r)).w(FUNC(nes_vt_soc_device::vt_dma_w));
map(0x4015, 0x4015).rw(FUNC(nes_vt_soc_device::psg1_4015_r), FUNC(nes_vt_soc_device::psg1_4015_w)); // PSG status / first control register
map(0x4016, 0x4016).rw(FUNC(nes_vt_soc_device::in0_r), FUNC(nes_vt_soc_device::in0_w));
map(0x4017, 0x4017).r(FUNC(nes_vt_soc_device::in1_r)).w(FUNC(nes_vt_soc_device::psg1_4017_w));
map(0x4034, 0x4034).w(FUNC(nes_vt_soc_device::vt03_4034_w));
map(0x4100, 0x410b).r(FUNC(nes_vt_soc_device::vt03_410x_r)).w(FUNC(nes_vt_soc_device::vt03_410x_w));
// 0x410c unused
map(0x410d, 0x410d).w(FUNC(nes_vt_soc_device::extra_io_control_w));
map(0x410e, 0x410e).rw(FUNC(nes_vt_soc_device::extrain_01_r), FUNC(nes_vt_soc_device::extraout_01_w));
map(0x410f, 0x410f).rw(FUNC(nes_vt_soc_device::extrain_23_r), FUNC(nes_vt_soc_device::extraout_23_w));
// 0x4114 RS232 timer (low)
// 0x4115 RS232 timer (high)
// 0x4116 unused
// 0x4117 unused
// 0x4118 unused
map(0x4119, 0x4119).r(FUNC(nes_vt_soc_device::rs232flags_region_r));
// 0x411a RS232 TX data
// 0x411b RS232 RX data
map(0x8000, 0xffff).rw(FUNC(nes_vt_soc_device::external_space_read), FUNC(nes_vt_soc_device::external_space_write));
map(0x6000, 0x7fff).ram();
}
WRITE_LINE_MEMBER(nes_vt_soc_device::apu_irq)
{
// TODO
// set_input_line(N2A03_APU_IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE);
}
uint8_t nes_vt_soc_device::apu_read_mem(offs_t offset)
{
// TODO
return 0x00;//mintf->program->read_byte(offset);
}
uint32_t nes_vt_soc_device::screen_update(screen_device& screen, bitmap_rgb32& bitmap, const rectangle& cliprect)
{
return m_ppu->screen_update(screen, bitmap, cliprect);
}
device_memory_interface::space_config_vector nes_vt_soc_device::memory_space_config() const
{
return space_config_vector {
std::make_pair(AS_PROGRAM, &m_space_config)
};
}
void nes_vt_soc_device::do_pal_timings_and_ppu_replacement(machine_config& config)
{
m_maincpu->set_clock(PALC_APU_CLOCK);
PPU_VT03PAL(config.replace(), m_ppu, N2A03_PAL_XTAL);
m_ppu->set_cpu_tag(m_maincpu);
m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
m_ppu->read_bg().set(FUNC(nes_vt_soc_device::chr_r));
m_ppu->read_sp().set(FUNC(nes_vt_soc_device::spr_r));
m_ppu->set_screen(m_screen);
m_screen->set_refresh_hz(50.0070);
m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66 / (PALC_APU_CLOCK.dvalue() / 1000000)) *
(ppu2c0x_device::VBLANK_LAST_SCANLINE_PAL - ppu2c0x_device::VBLANK_FIRST_SCANLINE_PALC + 1 + 2)));
m_screen->set_size(32 * 8, 312);
m_screen->set_visarea(0 * 8, 32 * 8 - 1, 0 * 8, 30 * 8 - 1);
}
void nes_vt_soc_device::device_add_mconfig(machine_config &config)
{
N2A03_CORE(config, m_maincpu, NTSC_APU_CLOCK); // Butterfly Catch in vgpocket confirms N2A03 core type, not 6502
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_device::nes_vt_map);
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_refresh_hz(60.0988);
m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66/(NTSC_APU_CLOCK.dvalue()/1000000)) *
(ppu2c0x_device::VBLANK_LAST_SCANLINE_NTSC-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)));
m_screen->set_size(32*8, 262);
m_screen->set_visarea(0*8, 32*8-1, 0*8, 30*8-1);
m_screen->set_screen_update(FUNC(nes_vt_soc_device::screen_update));
PPU_VT03(config, m_ppu, N2A03_NTSC_XTAL);
m_ppu->set_cpu_tag(m_maincpu);
m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
m_ppu->read_bg().set(FUNC(nes_vt_soc_device::chr_r));
m_ppu->read_sp().set(FUNC(nes_vt_soc_device::spr_r));
m_ppu->set_screen(m_screen);
/* sound hardware */
SPEAKER(config, "mono").front_center();
NES_APU_VT(config, m_apu, NTSC_APU_CLOCK);
m_apu->irq().set(FUNC(nes_vt_soc_device::apu_irq));
m_apu->mem_read().set(FUNC(nes_vt_soc_device::apu_read_mem));
m_apu->add_route(ALL_OUTPUTS, "mono", 0.50);
}
void nes_vt_soc_pal_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
do_pal_timings_and_ppu_replacement(config);
}
/***********************************************************************************************************************************************************/
/* 'Scramble' specifics */
/***********************************************************************************************************************************************************/
void nes_vt_soc_scramble_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
N2A03_CORE_SWAP_OP_D5_D6(config.replace(), m_maincpu, NTSC_APU_CLOCK); // Insect Chase in polmega confirms N2A03 core type, not 6502
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_scramble_device::nes_vt_map);
}
/***********************************************************************************************************************************************************/
/* '4K' specifics */
/***********************************************************************************************************************************************************/
void nes_vt_soc_4kram_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_4kram_device::nes_vt_map);
}
void nes_vt_soc_4kram_device::device_start()
{
nes_vt_soc_device::device_start();
m_upper_write_412c_callback.resolve_safe();
m_upper_read_412c_callback.resolve_safe(0xff);
m_upper_read_412d_callback.resolve_safe(0xff);
}
void nes_vt_soc_4kram_device::nes_vt_4k_ram_map(address_map &map)
{
nes_vt_soc_device::nes_vt_map(map);
map(0x0800, 0x0fff).ram();
// map(0x412c, 0x412c).rw(FUNC(nes_vt_soc_4kram_device::vtfp_412c_r, FUNC(nes_vt_soc_4kram_device::vtfp_412c_extbank_w)); // GPIO
// map(0x412d, 0x412d).r(FUNC(nes_vt_soc_4kram_device::vtfp_412d_r)); // GPIO
}
/***********************************************************************************************************************************************************/
/* 'CY' specifics (base = '4K') */
/***********************************************************************************************************************************************************/
void nes_vt_soc_4kram_cy_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_4kram_cy_device::nes_vt_cy_map);
}
void nes_vt_soc_4kram_cy_device::nes_vt_cy_map(address_map &map)
{
nes_vt_4k_ram_map(map);
map(0x41b0, 0x41bf).r(FUNC(nes_vt_soc_4kram_cy_device::vt03_41bx_r)).w(FUNC(nes_vt_soc_4kram_cy_device::vt03_41bx_w));
map(0x4130, 0x4136).r(FUNC(nes_vt_soc_4kram_cy_device::vt03_413x_r)).w(FUNC(nes_vt_soc_4kram_cy_device::vt03_413x_w));
map(0x414f, 0x414f).r(FUNC(nes_vt_soc_4kram_cy_device::vt03_414f_r));
map(0x415c, 0x415c).r(FUNC(nes_vt_soc_4kram_cy_device::vt03_415c_r));
map(0x48a0, 0x48af).r(FUNC(nes_vt_soc_4kram_cy_device::vt03_48ax_r)).w(FUNC(nes_vt_soc_4kram_cy_device::vt03_48ax_w));
}
void nes_vt_soc_4kram_cy_device::device_start()
{
nes_vt_soc_device::device_start();
save_item(NAME(m_413x));
}
uint8_t nes_vt_soc_4kram_cy_device::vt03_41bx_r(offs_t offset)
{
switch (offset)
{
case 0x07:
return 0x04;
default:
return 0x00;
}
}
void nes_vt_soc_4kram_cy_device::vt03_41bx_w(offs_t offset, uint8_t data)
{
logerror("vt03_41bx_w %02x %02x\n", offset, data);
}
uint8_t nes_vt_soc_4kram_cy_device::vt03_413x_r(offs_t offset)
{
logerror("vt03_413x_r %02x\n", offset);
return m_413x[offset];
}
void nes_vt_soc_4kram_cy_device::vt03_413x_w(offs_t offset, uint8_t data)
{
logerror("vt03_413x_w %02x %02x\n", offset, data);
// VT168 style ALU ??
m_413x[offset] = data;
if (offset == 0x5)
{
uint32_t res = uint32_t((m_413x[5] << 8) | m_413x[4]) * uint32_t((m_413x[1] << 8) | m_413x[0]);
m_413x[0] = res & 0xFF;
m_413x[1] = (res >> 8) & 0xFF;
m_413x[2] = (res >> 16) & 0xFF;
m_413x[3] = (res >> 24) & 0xFF;
m_413x[6] = 0x00;
}
else if (offset == 0x6)
{
/*uint32_t res = uint32_t((m_413x[5] << 8) | m_413x[4]) * uint32_t((m_413x[1] << 8) | m_413x[0]);
m_413x[0] = res & 0xFF;
m_413x[1] = (res >> 8) & 0xFF;
m_413x[2] = (res >> 16) & 0xFF;
m_413x[3] = (res >> 24) & 0xFF;*/
m_413x[6] = 0x00;
}
}
uint8_t nes_vt_soc_4kram_cy_device::vt03_414f_r()
{
return 0xff;
}
uint8_t nes_vt_soc_4kram_cy_device::vt03_415c_r()
{
return 0xff;
}
void nes_vt_soc_4kram_cy_device::vt03_48ax_w(offs_t offset, uint8_t data)
{
logerror("vt03_48ax_w %02x %02x\n", offset, data);
}
uint8_t nes_vt_soc_4kram_cy_device::vt03_48ax_r(offs_t offset)
{
switch (offset)
{
case 0x04:
return 0x01;
case 0x05:
return 0x01;
default:
return 0x00;
}
}
/***********************************************************************************************************************************************************/
/* 'BT' specifics (base = '4K') */
/***********************************************************************************************************************************************************/
void nes_vt_soc_4kram_bt_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_4kram_bt_device::nes_vt_bt_map);
}
void nes_vt_soc_4kram_bt_device::nes_vt_bt_map(address_map &map)
{
nes_vt_4k_ram_map(map);
map(0x412c, 0x412c).w(FUNC(nes_vt_soc_4kram_bt_device::vt03_412c_extbank_w));
}
void nes_vt_soc_4kram_bt_device::vt03_412c_extbank_w(uint8_t data)
{
m_upper_write_412c_callback(data);
}
/***********************************************************************************************************************************************************/
/* 'HH' specifics (base = '4K') */
/***********************************************************************************************************************************************************/
void nes_vt_soc_4kram_hh_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_4kram_hh_device::nes_vt_hh_map);
}
void nes_vt_soc_4kram_hh_device::vtfp_411d_w(uint8_t data)
{
// controls chram access and mapper emulation modes in later models
logerror("vtfp_411d_w %02x\n", data);
m_411d = data;
update_banks();
}
uint8_t nes_vt_soc_4kram_hh_device::vthh_414a_r()
{
return 0x80;
}
void nes_vt_soc_4kram_hh_device::nes_vt_hh_map(address_map &map)
{
nes_vt_soc_device::nes_vt_map(map);
map(0x0000, 0x1fff).mask(0x0fff).ram();
map(0x414a, 0x414a).r(FUNC(nes_vt_soc_4kram_hh_device::vthh_414a_r));
map(0x411d, 0x411d).w(FUNC(nes_vt_soc_4kram_hh_device::vtfp_411d_w));
}
/***********************************************************************************************************************************************************/
/* 'FP' specifics (base = 'HH') */ // used by fcpocket, dgun2573, rminitv
/***********************************************************************************************************************************************************/
void nes_vt_soc_4kram_fp_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
M6502_VTSCR(config.replace(), m_maincpu, NTSC_APU_CLOCK); // are these later chips N2A03 core, or 6502 core derived?
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_4kram_fp_device::nes_vt_fp_map);
}
uint8_t nes_vt_soc_4kram_fp_device::vtfp_4119_r()
{
// would be PAL/NTSC etc. in base system, maybe different here?
return 0x00;
}
void nes_vt_soc_4kram_fp_device::vtfp_411e_w(uint8_t data)
{
logerror("411e_w %02x\n", data);
if (data == 0x05)
dynamic_cast<m6502_vtscr&>(*m_maincpu).set_next_scramble(true);
else if (data == 0x00)
dynamic_cast<m6502_vtscr&>(*m_maincpu).set_next_scramble(false);
}
void nes_vt_soc_4kram_fp_device::vtfp_4a00_w(uint8_t data)
{
logerror("4a00_w %02x\n", data);
//if(data == 0x80)
// dynamic_cast<m6502_vtscr&>(*m_maincpu).set_scramble(false);
}
void nes_vt_soc_4kram_fp_device::vtfp_412c_extbank_w(uint8_t data)
{
m_upper_write_412c_callback(data);
}
uint8_t nes_vt_soc_4kram_fp_device::vtfp_412d_r()
{
return m_upper_read_412d_callback();
}
void nes_vt_soc_4kram_fp_device::vtfp_4242_w(uint8_t data)
{
logerror("vtfp_4242_w %02x\n", data);
m_4242 = data;
}
void nes_vt_soc_4kram_fp_device::nes_vt_fp_map(address_map &map)
{
nes_vt_soc_4kram_hh_device::nes_vt_hh_map(map);
map(0x4119, 0x4119).r(FUNC(nes_vt_soc_4kram_fp_device::vtfp_4119_r));
map(0x411e, 0x411e).w(FUNC(nes_vt_soc_4kram_fp_device::vtfp_411e_w)); // encryption toggle
map(0x412c, 0x412c).w(FUNC(nes_vt_soc_4kram_fp_device::vtfp_412c_extbank_w)); // GPIO
map(0x412d, 0x412d).r(FUNC(nes_vt_soc_4kram_fp_device::vtfp_412d_r)); // GPIO
map(0x4242, 0x4242).w(FUNC(nes_vt_soc_4kram_fp_device::vtfp_4242_w));
map(0x4a00, 0x4a00).w(FUNC(nes_vt_soc_4kram_fp_device::vtfp_4a00_w));
}
void nes_vt_soc_4kram_fp_pal_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_4kram_fp_device::device_add_mconfig(config);
do_pal_timings_and_ppu_replacement(config);
}
/***********************************************************************************************************************************************************/
/* 'DG' specifics (base = '4K') */
/***********************************************************************************************************************************************************/
void nes_vt_soc_8kram_dg_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_8kram_dg_device::nes_vt_dg_map);
}
void nes_vt_soc_8kram_dg_device::vt03_411c_w(uint8_t data)
{
logerror("vt03_411c_w %02x\n", data);
m_411c = data;
update_banks();
}
void nes_vt_soc_8kram_dg_device::nes_vt_dg_map(address_map &map)
{
nes_vt_soc_device::nes_vt_map(map);
map(0x0000, 0x1fff).ram();
map(0x411c, 0x411c).w(FUNC(nes_vt_soc_8kram_dg_device::vt03_411c_w));
}
/***********************************************************************************************************************************************************/
/* 'FA' specifics (base = 'DG') */ // used by fapocket
/***********************************************************************************************************************************************************/
void nes_vt_soc_8kram_fa_device::device_add_mconfig(machine_config& config)
{
nes_vt_soc_device::device_add_mconfig(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_8kram_fa_device::nes_vt_fa_map);
}
uint8_t nes_vt_soc_8kram_fa_device::vtfa_412c_r()
{
return m_upper_read_412c_callback();
}
void nes_vt_soc_8kram_fa_device::vtfa_412c_extbank_w(uint8_t data)
{
m_upper_write_412c_callback(data);
}
void nes_vt_soc_8kram_fa_device::vtfp_4242_w(uint8_t data)
{
logerror("vtfp_4242_w %02x\n", data);
m_4242 = data;
}
void nes_vt_soc_8kram_fa_device::nes_vt_fa_map(address_map &map)
{
nes_vt_soc_8kram_dg_device::nes_vt_dg_map(map);
map(0x412c, 0x412c).r(FUNC(nes_vt_soc_8kram_fa_device::vtfa_412c_r)).w(FUNC(nes_vt_soc_8kram_fa_device::vtfa_412c_extbank_w));
map(0x4242, 0x4242).w(FUNC(nes_vt_soc_8kram_fa_device::vtfp_4242_w));
}