summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99/peb/evpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99/peb/evpc.cpp')
-rw-r--r--src/devices/bus/ti99/peb/evpc.cpp498
1 files changed, 498 insertions, 0 deletions
diff --git a/src/devices/bus/ti99/peb/evpc.cpp b/src/devices/bus/ti99/peb/evpc.cpp
new file mode 100644
index 00000000000..b8cc021765e
--- /dev/null
+++ b/src/devices/bus/ti99/peb/evpc.cpp
@@ -0,0 +1,498 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/****************************************************************************
+ SNUG Enhanced Video Processor Card (EVPC)
+
+ This is an expansion card with an own v9938 video processor on board.
+ Later releases (EVPC2) can also be equipped with a v9958.
+
+ The EVPC is intended to be used
+ 1. with the TI-99/4A console
+ 2. with the SGCPU
+
+ For option 1, the console-internal VDP (TMS9928A) must be removed. This,
+ however, raises a problem, because the video interrupt must now be send
+ from the EVPC in the Peripheral Expansion Box to the console, but there is
+ no line in the PEB cable for this purpose.
+
+ To solve this issue, a separate cable is led from the EVPC to the console
+ which delivers the video interrupt, and also the GROM clock which is also
+ lost due to the removal of the internal VDP.
+
+ The SGCPU requires this card, as it does not offer any video processor.
+ In this configuration, the video interrupt cable is not required.
+
+ Also, the SGCPU does not offer a socket for the sound chip of the TI
+ console, and accordingly, the EVPC also gives the sound chip a new home.
+ Thus we assume that in the TI console (option 1) the sound chip has
+ also been removed.
+
+ Important note: The DSR (firmware) of the EVPC expects a memory expansion
+ to be present; otherwise, the configuration (using CALL EVPC) will crash.
+ There is no warning if the 32K expansion is not present.
+
+ Michael Zapf
+
+*****************************************************************************/
+
+#include "emu.h"
+#include "evpc.h"
+#include "speaker.h"
+
+#define EVPC_CRU_BASE 0x1400
+
+#define TRACE_ADDRESS 0
+#define TRACE_CRU 0
+#define TRACE_MEM 0
+
+#define NOVRAM_SIZE 256
+
+snug_enhanced_video_device::snug_enhanced_video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : ti_expansion_card_device(mconfig, TI99_EVPC, tag, owner, clock),
+ device_nvram_interface(mconfig, *this),
+ m_dsr_page(0),
+ m_inDsrArea(false),
+ m_novram_accessed(false),
+ m_palette_accessed(false),
+ m_RAMEN(false),
+ m_sound_accessed(false),
+ m_video_accessed(false),
+ m_intlevel(0),
+ m_dsrrom(nullptr),
+ m_novram(nullptr),
+ m_video(*this, VDP_TAG),
+ m_sound(*this, TISOUNDCHIP_TAG),
+ m_colorbus(*this, COLORBUS_TAG)
+{
+}
+
+SETADDRESS_DBIN_MEMBER( snug_enhanced_video_device::setaddress_dbin )
+{
+ // Do not allow setaddress for the debugger. It will mess up the
+ // setaddress/memory access pairs when the CPU enters wait states.
+ if (machine().side_effect_disabled()) return;
+
+ if (TRACE_ADDRESS) logerror("set address %04x, %s\n", offset, (state==ASSERT_LINE)? "read" : "write");
+
+ m_address = offset;
+ bool reading = (state==ASSERT_LINE);
+ int offbase = (m_address & 0xfc01);
+
+ // Sound
+ m_sound_accessed = ((m_address & 0xff01)==0x8400) && !reading;
+
+ // Video space
+ // 8800 / 8802 / 8804 / 8806
+ // 8c00 / 8c02 / 8c04 / 8c06
+ //
+ // Bits 1000 1w00 0000 0xx0
+ // Mask 1111 1000 0000 0001
+ m_video_accessed = ((offbase==0x8800) && reading) || ((offbase==0x8c00) && !reading);
+
+ // Read a byte in evpc DSR space
+ // 0x4000 - 0x5eff DSR (paged)
+ // 0x5f00 - 0x5fef NOVRAM
+ // 0x5ff0 - 0x5fff Palette
+ m_inDsrArea = ((m_address & 0xe000)==0x4000);
+ m_novram_accessed = ((m_address & 0xff00)==0x5f00);
+ m_palette_accessed = ((m_address & 0xfff0)==0x5ff0);
+
+ // Note that we check the selection in reverse order so that the overlap is avoided
+}
+
+//-------------------------------------------------
+// nvram_default - called to initialize NVRAM to
+// its default state
+//-------------------------------------------------
+
+void snug_enhanced_video_device::nvram_default()
+{
+ memset(m_novram.get(), 0, NOVRAM_SIZE);
+}
+
+//-------------------------------------------------
+// nvram_read - called to read NVRAM from the
+// .nv file
+//-------------------------------------------------
+
+void snug_enhanced_video_device::nvram_read(emu_file &file)
+{
+ file.read(m_novram.get(), NOVRAM_SIZE);
+}
+
+//-------------------------------------------------
+// nvram_write - called to write NVRAM to the
+// .nv file
+//-------------------------------------------------
+
+void snug_enhanced_video_device::nvram_write(emu_file &file)
+{
+ file.write(m_novram.get(), NOVRAM_SIZE);
+}
+
+/*
+ Read a byte in evpc DSR space, NOVRAM, Palette, or video
+ 0x4000 - 0x5eff DSR (paged)
+ 0x5f00 - 0x5fef NOVRAM
+ 0x5ff0 - 0x5fff Palette (5ff0, 5ff2, 5ff4, 5ff6)
+*/
+READ8Z_MEMBER(snug_enhanced_video_device::readz)
+{
+ if (m_selected && m_inDsrArea)
+ {
+ if (m_palette_accessed)
+ {
+ switch (m_address & 0x000f)
+ {
+ case 0:
+ // Palette Read Address Register
+ *value = m_palette.write_index;
+ break;
+
+ case 2:
+ // Palette Read Color Value
+ if (m_palette.read)
+ {
+ switch (m_palette.state)
+ {
+ case 0:
+ *value = m_palette.color[m_palette.read_index].red;
+ break;
+ case 1:
+ *value = m_palette.color[m_palette.read_index].green;
+ break;
+ case 2:
+ *value = m_palette.color[m_palette.read_index].blue;
+ break;
+ }
+ m_palette.state++;
+ if (m_palette.state == 3)
+ {
+ m_palette.state = 0;
+ m_palette.read_index++;
+ }
+ }
+ break;
+
+ case 4:
+ // Palette Read Pixel Mask
+ *value = m_palette.mask;
+ break;
+ case 6:
+ // Palette Read Address Register for Color Value
+ if (m_palette.read)
+ *value = 0;
+ else
+ *value = 3;
+ break;
+ }
+ return;
+ }
+
+ if (m_novram_accessed && m_RAMEN)
+ {
+ // NOVRAM
+ *value = m_novram[offset & 0x00ff];
+ return;
+ }
+
+ // DSR space
+ *value = m_dsrrom[(offset & 0x1fff) | (m_dsr_page<<13)];
+ return;
+ }
+
+ if (m_video_accessed)
+ {
+ *value = m_video->read(space, m_address>>1);
+ }
+}
+
+/*
+ Write a byte in evpc DSR space
+ 0x4000 - 0x5eff DSR (paged)
+ 0x5f00 - 0x5fef NOVRAM
+ 0x5ff0 - 0x5fff Palette (5ff8, 5ffa, 5ffc, 5ffe)
+*/
+WRITE8_MEMBER(snug_enhanced_video_device::write)
+{
+ if (m_selected && m_inDsrArea)
+ {
+ if (m_palette_accessed)
+ {
+ // Palette
+ if (TRACE_MEM) logerror("palette write %04x <- %02x\n", offset&0xffff, data);
+ switch (m_address & 0x000f)
+ {
+ case 0x08:
+ // Palette Write Address Register
+ if (TRACE_MEM) logerror("EVPC palette address write (for write access)\n");
+ m_palette.write_index = data;
+ m_palette.state = 0;
+ m_palette.read = 0;
+ break;
+
+ case 0x0a:
+ // Palette Write Color Value
+ if (TRACE_MEM) logerror("EVPC palette color write\n");
+ if (!m_palette.read)
+ {
+ switch (m_palette.state)
+ {
+ case 0:
+ m_palette.color[m_palette.write_index].red = data;
+ break;
+ case 1:
+ m_palette.color[m_palette.write_index].green = data;
+ break;
+ case 2:
+ m_palette.color[m_palette.write_index].blue = data;
+ break;
+ }
+ m_palette.state++;
+ if (m_palette.state == 3)
+ {
+ m_palette.state = 0;
+ m_palette.write_index++;
+ }
+ //evpc_palette.dirty = 1;
+ }
+ break;
+
+ case 0x0c:
+ // Palette Write Pixel Mask
+ if (TRACE_MEM) logerror("EVPC palette mask write\n");
+ m_palette.mask = data;
+ break;
+
+ case 0x0e:
+ // Palette Write Address Register for Color Value
+ if (TRACE_MEM) logerror("EVPC palette address write (for read access)\n");
+ m_palette.read_index = data;
+ m_palette.state = 0;
+ m_palette.read = 1;
+ break;
+
+ }
+ return;
+ }
+
+ if (m_novram_accessed && m_RAMEN)
+ {
+ // NOVRAM
+ m_novram[offset & 0x00ff] = data;
+ return;
+ }
+ }
+
+ if (m_video_accessed)
+ {
+ m_video->write(space, m_address>>1, data);
+ }
+
+ if (m_sound_accessed)
+ {
+ m_sound->write(space, 0, data);
+ }
+}
+
+/*
+ The CRU read handler. Read EVPC DIP switches
+ 0: Video timing (PAL/NTSC)
+ 1: -
+ 2: charset
+ 3: RAM shift
+ 4: -
+ 5: -
+ 6: -
+ 7: DIP or NOVRAM
+ Logic is inverted
+*/
+READ8Z_MEMBER(snug_enhanced_video_device::crureadz)
+{
+ if ((offset & 0xff00)==EVPC_CRU_BASE)
+ {
+ if ((offset & 0x00f0)==0) // offset 0 delivers bits 0-7 (address 00-0f)
+ {
+ *value = ~(ioport("EVPC-SW1")->read() | (ioport("EVPC-SW3")->read()<<2)
+ | (ioport("EVPC-SW4")->read()<<3) | (ioport("EVPC-SW8")->read()<<7));
+ }
+ }
+}
+
+/*
+ The CRU write handler.
+ Bit 0: Turn on DSR ROM
+ Bit 1: DSR page select (bit 0)
+ Bit 2: -
+ Bit 3: RAM enable
+ Bit 4: DSR page select (bit 2)
+ Bit 5: DSR page select (bit 1)
+ Bit 6: -
+ Bit 7: -
+*/
+WRITE8_MEMBER(snug_enhanced_video_device::cruwrite)
+{
+ if ((offset & 0xff00)==EVPC_CRU_BASE)
+ {
+ int bit = (offset >> 1) & 0x0f;
+ switch (bit)
+ {
+ case 0:
+ m_selected = (data!=0);
+ if (TRACE_CRU) logerror("Map DSR = %d\n", m_selected);
+ break;
+
+ case 1:
+ if (data!=0)
+ m_dsr_page |= 1;
+ else
+ m_dsr_page &= ~1;
+ break;
+
+ case 3:
+ m_RAMEN = (data!=0);
+ break;
+
+ case 4:
+ if (data!=0)
+ m_dsr_page |= 4;
+ else
+ m_dsr_page &= ~4;
+ break;
+
+ case 5:
+ if (data!=0)
+ m_dsr_page |= 2;
+ else
+ m_dsr_page &= ~2;
+ break;
+
+ case 2:
+ case 6:
+ case 7:
+ break;
+ }
+ }
+}
+
+/*
+ READY line for the sound chip
+*/
+WRITE_LINE_MEMBER( snug_enhanced_video_device::ready_line )
+{
+ m_slot->set_ready(state);
+}
+
+void snug_enhanced_video_device::device_start()
+{
+ m_dsrrom = memregion(DSRROM)->base();
+ m_novram = std::make_unique<uint8_t[]>(NOVRAM_SIZE);
+ m_console_conn = downcast<evpc_clock_connector*>(machine().device(EVPC_CONN_TAG));
+ save_item(NAME(m_address));
+ save_item(NAME(m_dsr_page));
+ save_item(NAME(m_inDsrArea));
+ save_item(NAME(m_novram_accessed));
+ save_item(NAME(m_palette_accessed));
+ save_item(NAME(m_RAMEN));
+ save_item(NAME(m_sound_accessed));
+ save_item(NAME(m_video_accessed));
+ save_item(NAME(m_intlevel));
+}
+
+void snug_enhanced_video_device::device_reset()
+{
+ m_select_mask = 0x7e000;
+ m_select_value = 0x74000;
+ m_dsr_page = 0;
+ m_RAMEN = false;
+ m_selected = false;
+}
+
+void snug_enhanced_video_device::device_stop()
+{
+ m_novram = nullptr;
+}
+
+/*
+ This is the extra cable running from the EVPC card right into the TI console.
+ It delivers the VDP interrupt and the GROM clock.
+
+ For the SGCPU, the signal is delivered by the LCP line.
+*/
+WRITE_LINE_MEMBER( snug_enhanced_video_device::video_interrupt_in )
+{
+ // This method is frequently called without level change, so we only
+ // react on changes
+ if (state != m_intlevel)
+ {
+ m_intlevel = state;
+ if (m_console_conn != nullptr) m_console_conn->vclock_line(state);
+ else m_slot->lcp_line(state);
+
+ if (state!=0) m_colorbus->poll();
+ }
+}
+
+ROM_START( ti99_evpc )
+ ROM_REGION(0x10000, DSRROM, 0)
+ ROM_LOAD("evpc_dsr.u21", 0, 0x10000, CRC(a062b75d) SHA1(6e8060f86e3bb9c36f244d88825e3fe237bfe9a9)) /* evpc DSR ROM */
+ROM_END
+
+MACHINE_CONFIG_FRAGMENT( ti99_evpc )
+ // video hardware
+ MCFG_V9938_ADD(VDP_TAG, SCREEN_TAG, 0x20000, XTAL_21_4772MHz) /* typical 9938 clock, not verified */
+ MCFG_V99X8_INTERRUPT_CALLBACK(WRITELINE(snug_enhanced_video_device, video_interrupt_in))
+ MCFG_V99X8_SCREEN_ADD_NTSC(SCREEN_TAG, VDP_TAG, XTAL_21_4772MHz)
+
+ // Sound hardware
+ MCFG_SPEAKER_STANDARD_MONO("sound_out")
+ MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN94624, 3579545/8) /* 3.579545 MHz */
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "sound_out", 0.75)
+ MCFG_SN76496_READY_HANDLER( WRITELINE(snug_enhanced_video_device, ready_line) )
+
+ // Mouse connected to the color bus of the v9938
+ MCFG_COLORBUS_MOUSE_ADD( COLORBUS_TAG )
+
+MACHINE_CONFIG_END
+
+/*
+ Input ports for the EPVC
+*/
+INPUT_PORTS_START( ti99_evpc )
+ PORT_START( "EVPC-SW1" )
+ PORT_DIPNAME( 0x01, 0x00, "EVPC video mode" ) PORT_CONDITION( "EVPC-SW8", 0x01, EQUALS, 0x00 )
+ PORT_DIPSETTING( 0x00, "PAL" )
+ PORT_DIPSETTING( 0x01, "NTSC" )
+
+ PORT_START( "EVPC-SW3" )
+ PORT_DIPNAME( 0x01, 0x00, "EVPC charset" ) PORT_CONDITION( "EVPC-SW8", 0x01, EQUALS, 0x00 )
+ PORT_DIPSETTING( 0x00, DEF_STR( International ))
+ PORT_DIPSETTING( 0x01, DEF_STR( German ))
+
+ PORT_START( "EVPC-SW4" )
+ PORT_DIPNAME( 0x01, 0x00, "EVPC VDP RAM" ) PORT_CONDITION( "EVPC-SW8", 0x01, EQUALS, 0x00 )
+ PORT_DIPSETTING( 0x00, "shifted" )
+ PORT_DIPSETTING( 0x01, "not shifted" )
+
+ PORT_START( "EVPC-SW8" )
+ PORT_DIPNAME( 0x01, 0x00, "EVPC Configuration" )
+ PORT_DIPSETTING( 0x00, "DIP" )
+ PORT_DIPSETTING( 0x01, "NOVRAM" )
+INPUT_PORTS_END
+
+const tiny_rom_entry *snug_enhanced_video_device::device_rom_region() const
+{
+ return ROM_NAME( ti99_evpc );
+}
+
+ioport_constructor snug_enhanced_video_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(ti99_evpc);
+}
+
+machine_config_constructor snug_enhanced_video_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( ti99_evpc );
+}
+
+DEFINE_DEVICE_TYPE(TI99_EVPC, snug_enhanced_video_device, "ti99_evpc", "SNUG Enhanced Video Processor Card")