// 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(0x2000); save_pointer(NAME(m_ntram), 0x2000); m_chrram = std::make_unique(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(*m_maincpu).set_next_scramble(true); else if (data == 0x00) dynamic_cast(*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(*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)); }