summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/nes/subor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/nes/subor.cpp')
-rw-r--r--src/devices/bus/nes/subor.cpp325
1 files changed, 325 insertions, 0 deletions
diff --git a/src/devices/bus/nes/subor.cpp b/src/devices/bus/nes/subor.cpp
new file mode 100644
index 00000000000..65bf2e18e7e
--- /dev/null
+++ b/src/devices/bus/nes/subor.cpp
@@ -0,0 +1,325 @@
+// license:BSD-3-Clause
+// copyright-holders:Kaz
+/***********************************************************************************************************
+
+ NES/Famicom cartridge emulation for Subor PCBs
+
+ TODO:
+ - Implement Type 2 variant for Subor Karaoke.
+ (Subor Karaoke updates banks differently.)
+ - Check and verify CHR banking in Type 2 boards
+ - Investigate connection with DANCE 2000 board; likely made by ex-Subor staff!
+
+ ***********************************************************************************************************/
+
+#include "emu.h"
+#include "subor.h"
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(...) do { if (VERBOSE) logerror(__VA_ARGS__); } while (0)
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+const device_type NES_SUBOR0 = device_creator<nes_subor0_device>;
+const device_type NES_SUBOR1 = device_creator<nes_subor1_device>;
+const device_type NES_SUBOR2 = device_creator<nes_subor2_device>;
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// nes_subor0_device - constructor
+//-------------------------------------------------
+
+nes_subor0_device::nes_subor0_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : nes_nrom_device(mconfig, NES_SUBOR0, "NES Cart Subor Type 0 PCB", tag, owner, clock, "nes_subor0", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// nes_subor1_device - constructor
+//-------------------------------------------------
+
+nes_subor1_device::nes_subor1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : nes_nrom_device(mconfig, NES_SUBOR1, "NES Cart Subor Type 1 PCB", tag, owner, clock, "nes_subor1", __FILE__)
+{
+}
+
+//-------------------------------------------------
+// nes_subor2_device - constructor
+//-------------------------------------------------
+
+nes_subor2_device::nes_subor2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : nes_nrom_device(mconfig, NES_SUBOR2, "NES Cart Subor Type 2 PCB", tag, owner, clock, "nes_subor2", __FILE__),
+ m_switch_reg(0),
+ m_bank_reg(0),
+ m_chr_banking(0),
+ m_page(0)
+{
+}
+
+/*-------------------------------------------------
+ device_start
+-------------------------------------------------*/
+
+void nes_subor0_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_subor1_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_subor2_device::device_start()
+{
+ common_start();
+
+ save_item(NAME(m_switch_reg));
+ save_item(NAME(m_bank_reg));
+ save_item(NAME(m_chr_banking));
+}
+
+/*-------------------------------------------------
+ pcb_reset
+-------------------------------------------------*/
+
+void nes_subor0_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0x20);
+ chr8(0, m_chr_source);
+
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_subor1_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0x07);
+ chr8(0, m_chr_source);
+
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_subor2_device::pcb_reset()
+{
+ m_switch_reg = 0;
+ m_bank_reg = 0;
+ m_chr_banking = true;
+ m_page = 0;
+
+ prg16_89ab(0);
+ prg16_cdef(0);
+}
+
+/*-------------------------------------------------
+ mapper specific handlers
+-------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Subor educational cartridge board Type 0:
+ iNES Mapper 167
+
+ Subor educational cartridge board Type 1:
+ iNES Mapper 166
+
+ Subor educational cartridge board Type 2:
+ No iNES mapper as of yet.
+
+ Notes on Type 2 variants:
+ Implementation based upon VirtuaNESUp source code
+ and studying VirtuaNESEx. The latter makes the
+ read at 0x5300 return 0x8F, perhaps as a form
+ of copy protection?
+
+ There are two revisions of Type 2 that are
+ currently known;
+
+ Subor v5.0- 16k/32k PRG banking combo
+
+ Subor Karaoke- 32k PRG banking
+
+-------------------------------------------------*/
+
+/*-------------------------------------------------
+ ppu_latch
+-------------------------------------------------*/
+
+void nes_subor2_device::ppu_latch(offs_t offset)
+{
+ /* CHR banks are conditionally changed midframe */
+ /* If this is split off onto the external PPU latch, every edge case works */
+ if (m_chr_banking)
+ {
+ if ( (m_page == 2) || (m_page == 1 && m_switch_reg == 2) )
+ {
+ chr4_0(1, CHRRAM);
+ }
+ else
+ {
+ chr4_0(0, CHRRAM);
+ }
+ }
+}
+
+/*-------------------------------------------------
+ nt
+-------------------------------------------------*/
+
+READ8_MEMBER(nes_subor2_device::nt_r)
+{
+ int page = ((offset & 0xc00) >> 10);
+
+ /* Nametable reads report the current page; this seems to work without issues */
+ m_page = page;
+
+ return m_nt_access[page][offset & 0x3ff];
+}
+
+/*-------------------------------------------------
+ update_banks
+-------------------------------------------------*/
+
+void nes_subor2_device::update_banks()
+{
+ switch (m_switch_reg)
+ {
+ case 0x00:
+ case 0x02:
+ m_chr_banking = true;
+ set_nt_mirroring(PPU_MIRROR_VERT);
+ break;
+ case 0x01:
+ case 0x03:
+ m_chr_banking = true;
+ set_nt_mirroring(PPU_MIRROR_HORZ);
+ break;
+ case 0x05:
+ /* Subor v11.0 needs this, and VirtuaNESEx keeps it specific to this cart */
+ /* But leaving it as it is doesn't seem to be an issue for now */
+ m_chr_banking = false;
+ set_nt_mirroring(PPU_MIRROR_HORZ);
+ break;
+ }
+
+ if (m_switch_reg >= 0x04)
+ {
+ prg32(m_bank_reg);
+ }
+ else
+ {
+ prg16_89ab(m_bank_reg);
+ prg16_cdef(0);
+ }
+}
+
+/*-------------------------------------------------
+ read
+-------------------------------------------------*/
+
+READ8_MEMBER(nes_subor2_device::read_l)
+{
+ LOG_MMC("subor2 read_l, offset: %04x\n", offset);
+
+ if (offset == 0x1200)
+ {
+ return 0x8F;
+ }
+ return m_open_bus;
+}
+
+/*-------------------------------------------------
+ write
+-------------------------------------------------*/
+
+WRITE8_MEMBER(nes_subor0_device::write_h)
+{
+ uint8_t subor_helper1, subor_helper2;
+ LOG_MMC("subor0 write_h, offset: %04x, data: %02x\n", offset, data);
+
+ m_reg[(offset >> 13) & 0x03] = data;
+ subor_helper1 = ((m_reg[0] ^ m_reg[1]) << 1) & 0x20;
+ subor_helper2 = ((m_reg[2] ^ m_reg[3]) << 0) & 0x1f;
+
+ if (m_reg[1] & 0x08)
+ {
+ subor_helper1 += subor_helper2 & 0xfe;
+ subor_helper2 = subor_helper1;
+ subor_helper1 += 1;
+ }
+ else if (m_reg[1] & 0x04)
+ {
+ subor_helper2 += subor_helper1;
+ subor_helper1 = 0x1f;
+ }
+ else
+ {
+ subor_helper1 += subor_helper2;
+ subor_helper2 = 0x20;
+ }
+
+ prg16_89ab(subor_helper1);
+ prg16_cdef(subor_helper2);
+}
+
+WRITE8_MEMBER(nes_subor1_device::write_h)
+{
+ uint8_t subor_helper1, subor_helper2;
+ LOG_MMC("subor1 write_h, offset: %04x, data: %02x\n", offset, data);
+
+ m_reg[(offset >> 13) & 0x03] = data;
+ subor_helper1 = ((m_reg[0] ^ m_reg[1]) << 1) & 0x20;
+ subor_helper2 = ((m_reg[2] ^ m_reg[3]) << 0) & 0x1f;
+
+ if (m_reg[1] & 0x08)
+ {
+ subor_helper1 += subor_helper2 & 0xfe;
+ subor_helper2 = subor_helper1;
+ subor_helper2 += 1;
+ }
+ else if (m_reg[1] & 0x04)
+ {
+ subor_helper2 += subor_helper1;
+ subor_helper1 = 0x1f;
+ }
+ else
+ {
+ subor_helper1 += subor_helper2;
+ subor_helper2 = 0x07;
+ }
+
+ prg16_89ab(subor_helper1);
+ prg16_cdef(subor_helper2);
+}
+
+WRITE8_MEMBER(nes_subor2_device::write_l)
+{
+ LOG_MMC("subor2 write_l, offset: %04x, data: %02x\n", offset, data);
+
+ switch (offset)
+ {
+ case 0x0F00:
+ m_bank_reg = data;
+ update_banks();
+ break;
+ case 0x1100:
+ m_switch_reg = data & 7;
+ update_banks();
+ break;
+ }
+} \ No newline at end of file