summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/nes_vt_soc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/nes_vt_soc.cpp')
-rw-r--r--src/mame/machine/nes_vt_soc.cpp1587
1 files changed, 1587 insertions, 0 deletions
diff --git a/src/mame/machine/nes_vt_soc.cpp b/src/mame/machine/nes_vt_soc.cpp
new file mode 100644
index 00000000000..c8ac9ade429
--- /dev/null
+++ b/src/mame/machine/nes_vt_soc.cpp
@@ -0,0 +1,1587 @@
+// 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_dg", "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;
+}
+
+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, read8_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::chr_r)), write8_delegate(*m_cartslot->m_cart, FUNC(device_nes_cart_interface::chr_w)));
+ m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x2000, 0x3eff, read8_delegate(*this, FUNC(nes_vt_soc_device::nt_r)), write8_delegate(*this, FUNC(nes_vt_soc_device::nt_w)));
+ m_ppu->space(AS_PROGRAM).install_readwrite_handler(0, 0x1fff, read8_delegate(*this, FUNC(nes_vt_soc_device::chr_r)), write8_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()
+{
+ //uint32_t amod = m_ahigh >> 13;
+
+ 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] = ((/*amod |*/ get_banks(bank)) );
+
+ // a000-bfff
+ bank = m_410x[0x8];
+ m_bankaddr[1] = ((/*amod |*/ 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] = ((/*amod |*/ get_banks(bank)) );
+
+ // e000 - ffff
+ bank = m_initial_e000_bank;
+ m_bankaddr[3] = ((/*amod |*/ 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;
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::vt03_410x_w)
+{
+ scrambled_410x_w(offset, data);
+}
+
+READ8_MEMBER(nes_vt_soc_device::vt03_410x_r)
+{
+ 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];
+ 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;
+ }
+}
+
+
+
+READ8_MEMBER(nes_vt_soc_device::spr_r)
+{
+ 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);
+ }
+}
+
+READ8_MEMBER(nes_vt_soc_device::chr_r)
+{
+ if (m_4242 & 0x1 || m_411d & 0x04)
+ {
+ 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);
+ }
+}
+
+
+WRITE8_MEMBER(nes_vt_soc_device::chr_w)
+{
+ if (m_4242 & 0x1 || m_411d & 0x04)
+ {
+ logerror("vram write %04x %02x\n", offset, data);
+ m_chrram[offset] = 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 */
+READ8_MEMBER(nes_vt_soc_device::nt_r)
+{
+ return m_ntram[decode_nt_addr(offset)];
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::nt_w)
+{
+ //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 /*m_ahigh |*/ 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
+
+ is this always there with VT based games? it maps where mappers would be on a NES cartridge
+ but then seems to be able to alter internal state of extended PPU registers, which is awkward
+*/
+
+void nes_vt_soc_device::scrambled_8000_w(address_space& space, uint16_t offset, uint8_t data)
+{
+ offset &= 0x7fff;
+
+ uint16_t addr = m_real_access_address; // we need the actual write address, not the translated one, to keep bittboy happy
+ if ((m_411d & 0x01) && (m_411d & 0x03))
+ {
+ //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)
+ {
+ //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)
+ {
+ //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
+ {
+ //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(space, 1, data);
+ }
+ else if ((addr >= 0xC000) && (addr < 0xE000) && (addr & 0x01))
+ {
+ // IRQ reload
+ vt03_410x_w(space, 2, data);
+ }
+ else if ((addr >= 0xE000) && !(addr & 0x01))
+ {
+ // IRQ disable
+ vt03_410x_w(space, 3, data);
+ }
+ else if ((addr >= 0xE000) && (addr & 0x01))
+ {
+ // IRQ enable
+ vt03_410x_w(space, 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;
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::vt03_8000_mapper_w)
+{
+ scrambled_8000_w(space, 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 */
+
+READ8_MEMBER(nes_vt_soc_device::psg1_4014_r)
+{
+ //return m_apu->read(0x14);
+ return 0x00;
+}
+
+READ8_MEMBER(nes_vt_soc_device::psg1_4015_r)
+{
+ return m_apu->read(0x15);
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::psg1_4015_w)
+{
+ m_apu->write(0x15, data);
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::psg1_4017_w)
+{
+ m_apu->write(0x17, data);
+}
+
+// early units (VT03?) have a DMA bug in NTSC mode
+WRITE8_MEMBER(nes_vt_soc_device::vt_dma_w)
+{
+ 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;
+ }
+ else if ((dma_mode == 1) && ((m_ppu->get_vram_dest() & 0xFF00) == 0x3F01) && !(m_ppu->get_201x_reg(0x1) & 0x80))
+ {
+ // Legacy mode for DGUN-2573 compat
+ m_ppu->set_vram_dest(0x3F00);
+ m_ppu->set_palette_mode(PAL_MODE_VT0x);
+ }
+
+ 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));
+}
+
+
+WRITE8_MEMBER(nes_vt_soc_device::vt03_4034_w)
+{
+ logerror("vt03_4034_w %02x\n", data);
+ m_vdma_ctrl = data;
+}
+
+READ8_MEMBER(nes_vt_soc_device::in0_r)
+{
+ return m_read_0_callback();
+}
+
+READ8_MEMBER(nes_vt_soc_device::in1_r)
+{
+ return m_read_1_callback();
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::in0_w)
+{
+ m_write_0_callback(offset, data);
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::extra_io_control_w)
+{
+ /*
+ 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);
+}
+
+READ8_MEMBER(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);
+}
+
+READ8_MEMBER(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);
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::extraout_01_w)
+{
+ // TODO: use callbacks for this as output can be hooked up to anything
+ logerror("%s: extraout_01_w %02x\n", machine().describe_context(), data);
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::extraout_23_w)
+{
+ // TODO: use callbacks for this as output can be hooked up to anything
+ logerror("%s: extraout_23_w %02x\n", machine().describe_context(), data);
+}
+
+READ8_MEMBER(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;
+}
+
+
+READ8_MEMBER(nes_vt_soc_device::external_space_read)
+{
+ address_space& spc = this->space(AS_PROGRAM);
+ int bank = (offset & 0x6000) >> 13;
+ int address = (m_bankaddr[bank] * 0x2000) + (offset & 0x1fff);
+ m_real_access_address = offset + 0x8000;
+ return spc.read_byte(address);
+}
+
+WRITE8_MEMBER(nes_vt_soc_device::external_space_write)
+{
+ address_space& spc = this->space(AS_PROGRAM);
+ int bank = (offset & 0x6000) >> 13;
+ int address = (m_bankaddr[bank] * 0x2000) + (offset&0x1fff);
+ m_real_access_address = offset + 0x8000;
+ spc.write_byte(address, data);
+};
+
+void nes_vt_soc_device::nes_vt_map(address_map &map)
+{
+ map(0x0000, 0x07ff).ram();
+
+ // 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);
+}
+
+READ8_MEMBER(nes_vt_soc_device::apu_read_mem)
+{
+ // 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)
+{
+ M6502(config, m_maincpu, NTSC_APU_CLOCK);
+ 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();
+
+ /* this should actually be a custom *almost* doubled up APU, however requires more thought
+ than just using 2 APUs as registers in the 2nd one affect the PCM channel mode but the
+ DMA control still comes from the 1st, but in the new mode, sound always outputs via the
+ 2nd. Probably need to split the APU into interface and sound gen logic. */
+ NES_APU(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);
+
+ M6502_SWAP_OP_D5_D6(config.replace(), m_maincpu, NTSC_APU_CLOCK);
+ 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));
+}
+
+
+READ8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_41bx_r)
+{
+ switch (offset)
+ {
+ case 0x07:
+ return 0x04;
+ default:
+ return 0x00;
+ }
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_41bx_w)
+{
+ logerror("vt03_41bx_w %02x %02x\n", offset, data);
+}
+
+READ8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_413x_r)
+{
+ logerror("vt03_413x_r %02x\n", offset);
+ return m_413x[offset];
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_413x_w)
+{
+ 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;
+ }
+}
+
+READ8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_414f_r)
+{
+ return 0xff;
+}
+
+READ8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_415c_r)
+{
+ return 0xff;
+}
+
+
+WRITE8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_48ax_w)
+{
+ logerror("vt03_48ax_w %02x %02x\n", offset, data);
+}
+
+READ8_MEMBER(nes_vt_soc_4kram_cy_device::vt03_48ax_r)
+{
+ 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));
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_bt_device::vt03_412c_extbank_w)
+{
+ 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);
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_hh_device::vtfp_411d_w)
+{
+ // controls chram access and mapper emulation modes in later models
+ logerror("vtfp_411d_w %02x\n", data);
+ m_411d = data;
+ update_banks();
+}
+
+READ8_MEMBER(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);
+ m_maincpu->set_addrmap(AS_PROGRAM, &nes_vt_soc_4kram_fp_device::nes_vt_fp_map);
+}
+
+
+READ8_MEMBER(nes_vt_soc_4kram_fp_device::vtfp_4119_r)
+{
+ // would be PAL/NTSC etc. in base system, maybe different here?
+ return 0x00;
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_fp_device::vtfp_411e_w)
+{
+ 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);
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_fp_device::vtfp_4a00_w)
+{
+ logerror("4a00_w %02x\n", data);
+ //if(data == 0x80)
+ // dynamic_cast<m6502_vtscr&>(*m_maincpu).set_scramble(false);
+}
+
+
+WRITE8_MEMBER(nes_vt_soc_4kram_fp_device::vtfp_412c_extbank_w)
+{
+ m_upper_write_412c_callback(data);
+}
+
+READ8_MEMBER(nes_vt_soc_4kram_fp_device::vtfp_412d_r)
+{
+ return m_upper_read_412d_callback();
+}
+
+WRITE8_MEMBER(nes_vt_soc_4kram_fp_device::vtfp_4242_w)
+{
+ 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);
+}
+
+WRITE8_MEMBER(nes_vt_soc_8kram_dg_device::vt03_411c_w)
+{
+ 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);
+}
+
+READ8_MEMBER(nes_vt_soc_8kram_fa_device::vtfa_412c_r)
+{
+ return m_upper_read_412c_callback();
+}
+
+WRITE8_MEMBER(nes_vt_soc_8kram_fa_device::vtfa_412c_extbank_w)
+{
+ m_upper_write_412c_callback(data);
+
+}
+
+WRITE8_MEMBER(nes_vt_soc_8kram_fa_device::vtfp_4242_w)
+{
+ 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));
+}
+