ti99: Added I/O port Splitter device.

This commit is contained in:
Michael Zapf 2025-03-02 21:36:38 +01:00
parent 4035ef4a59
commit f962ece5fd
7 changed files with 346 additions and 72 deletions

View File

@ -4120,6 +4120,8 @@ if (BUSES["TI99"]~=null) then
MAME_DIR .. "src/devices/bus/ti99/internal/genkbd.h",
MAME_DIR .. "src/devices/bus/ti99/internal/ioport.cpp",
MAME_DIR .. "src/devices/bus/ti99/internal/ioport.h",
MAME_DIR .. "src/devices/bus/ti99/internal/splitter.cpp",
MAME_DIR .. "src/devices/bus/ti99/internal/splitter.h",
MAME_DIR .. "src/devices/bus/ti99/colorbus/busmouse.cpp",
MAME_DIR .. "src/devices/bus/ti99/colorbus/busmouse.h",
MAME_DIR .. "src/devices/bus/ti99/colorbus/colorbus.cpp",

View File

@ -78,6 +78,7 @@
#include "emu.h"
#include "ioport.h"
#include "splitter.h"
#include "bus/ti99/peb/peribox.h"
DEFINE_DEVICE_TYPE(TI99_IOPORT, bus::ti99::internal::ioport_device, "ti99_ioport", "TI-99 I/O Port")
@ -174,9 +175,18 @@ void ioport_attached_device::set_ready(int state)
void ti99_ioport_options_plain(device_slot_interface &device)
{
device.option_add("peb", TI99_PERIBOX);
device.option_add("splitter", TI99_IOSPLIT);
}
void ti99_ioport_options_evpc(device_slot_interface &device)
{
device.option_add("peb", TI99_PERIBOX_EV);
device.option_add("splitter", TI99_IOSPLIT);
}
// Used for the splitter (to avoid getting multiple EVPCs in the system)
void ti99_ioport_options_evpc1(device_slot_interface &device)
{
device.option_add("peb", TI99_PERIBOX_EV1);
device.option_add("splitter", TI99_IOSPLIT);
}

View File

@ -103,5 +103,6 @@ DECLARE_DEVICE_TYPE_NS(TI99_IOPORT, bus::ti99::internal, ioport_device)
void ti99_ioport_options_plain(device_slot_interface &device);
void ti99_ioport_options_evpc(device_slot_interface &device);
void ti99_ioport_options_evpc1(device_slot_interface &device);
#endif /* __TI99IOPORT__ */

View File

@ -0,0 +1,190 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
I/O port splitter
The I/O port splitter connects to the TI-99/4(A) console at its I/O port
and provides two I/O ports. That way, two Peripheral Expansion boxes or
one PEB and a sidecar chain may be connected.
| Port 2
v
+---+
+----------------+ | |
| TI-99/4(A) |---+ +--+
+------------+---+ Splitter | <-- Port 1
| oooooooooo | |----------+
| oooooooooo | |
+-----------------
The splitter was designed 2015 by Jim Fetzner (as Tekumel Software)
March 2025, Michael Zapf
*****************************************************************************/
#include "emu.h"
#include "ioport.h"
#include "splitter.h"
#include "bus/ti99/peb/peribox.h"
#define LOG_WARN (1U << 1) // Warnings
#define LOG_CONFIG (1U << 2) // Configuration
#define LOG_INT (1U << 3)
#define LOG_READY (1U << 4)
#define VERBOSE (LOG_CONFIG | LOG_WARN)
#include "logmacro.h"
DEFINE_DEVICE_TYPE(TI99_IOSPLIT, bus::ti99::internal::ioport_splitter_device, "ti99_iosplit", "TI-99 I/O Port Splitter")
namespace bus::ti99::internal {
#define PORT1 "port1"
#define PORT2 "port2"
ioport_splitter_device::ioport_splitter_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: ioport_attached_device(mconfig, type, tag, owner, clock),
m_port1(*this, PORT1),
m_port2(*this, PORT2)
{
}
ioport_splitter_device::ioport_splitter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: ioport_splitter_device(mconfig, TI99_IOSPLIT, tag, owner, clock)
{
}
void ioport_splitter_device::readz(offs_t offset, uint8_t *value)
{
// With a proper configuration, only one device on one of the branches
// will respond.
if (m_port1 != nullptr)
m_port1->readz(offset, value);
if (m_port2 != nullptr)
m_port2->readz(offset, value);
}
void ioport_splitter_device::write(offs_t offset, uint8_t data)
{
if (m_port1 != nullptr)
m_port1->write(offset, data);
if (m_port2 != nullptr)
m_port2->write(offset, data);
}
void ioport_splitter_device::setaddress_dbin(offs_t offset, int state)
{
if (m_port1 != nullptr)
m_port1->setaddress_dbin(offset, state);
if (m_port2 != nullptr)
m_port2->setaddress_dbin(offset, state);
}
void ioport_splitter_device::crureadz(offs_t offset, uint8_t *value)
{
if (m_port1 != nullptr)
m_port1->crureadz(offset, value);
if (m_port2 != nullptr)
m_port2->crureadz(offset, value);
}
void ioport_splitter_device::cruwrite(offs_t offset, uint8_t data)
{
if (m_port1 != nullptr)
m_port1->cruwrite(offset, data);
if (m_port2 != nullptr)
m_port2->cruwrite(offset, data);
}
void ioport_splitter_device::memen_in(int state)
{
if (m_port1 != nullptr)
m_port1->memen_in(state);
if (m_port2 != nullptr)
m_port2->memen_in(state);
}
void ioport_splitter_device::msast_in(int state)
{
if (m_port1 != nullptr)
m_port1->msast_in(state);
if (m_port2 != nullptr)
m_port2->msast_in(state);
}
void ioport_splitter_device::clock_in(int state)
{
if (m_port1 != nullptr)
m_port1->clock_in(state);
if (m_port2 != nullptr)
m_port2->clock_in(state);
}
void ioport_splitter_device::reset_in(int state)
{
if (m_port1 != nullptr)
m_port1->reset_in(state);
if (m_port2 != nullptr)
m_port2->reset_in(state);
}
template<int port>
void ioport_splitter_device::extint(int state)
{
LOGMASKED(LOG_INT, "propagating INTA from port %d to console: %d\n", port, state);
if (state==ASSERT_LINE)
m_inta_flag |= port; // 1 or 2
else
m_inta_flag &= ~port; // 1 or 2
set_extint((m_inta_flag != 0)? ASSERT_LINE : CLEAR_LINE);
}
template<int port>
void ioport_splitter_device::ready(int state)
{
LOGMASKED(LOG_READY, "Incoming READY=%d from port %d\n", state, port);
// We store the inverse state
if (state==CLEAR_LINE)
m_ready_flag |= port; // 1 or 2
else
m_ready_flag &= ~port; // 1 or 2
set_ready((m_ready_flag != 0)? CLEAR_LINE : ASSERT_LINE);
}
void ioport_splitter_device::device_start()
{
LOG("Starting I/O Port Splitter\n");
}
void ioport_splitter_device::device_config_complete()
{
m_inta_flag = 0;
m_ready_flag = 0;
}
void ioport_splitter_device::device_add_mconfig(machine_config &config)
{
TI99_IOPORT(config, m_port1, 1, ti99_ioport_options_evpc1, nullptr);
TI99_IOPORT(config, m_port2, 2, ti99_ioport_options_evpc1, nullptr);
m_port1->extint_cb().set(FUNC(ioport_splitter_device::extint<1>));
m_port2->extint_cb().set(FUNC(ioport_splitter_device::extint<2>));
m_port1->ready_cb().set(FUNC(ioport_splitter_device::ready<1>));
m_port2->ready_cb().set(FUNC(ioport_splitter_device::ready<2>));
}
} // end namespace bus::ti99::internal

View File

@ -0,0 +1,65 @@
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************
I/O port splitter
This plugs into the I/O port of the console as an
ioport_attached_device, and it offers two new I/O ports.
*****************************************************************************/
#ifndef MAME_BUS_TI99_INTERNAL_SPLITTER_H
#define MAME_BUS_TI99_INTERNAL_SPLITTER_H
#pragma once
#include "ioport.h"
namespace bus::ti99::internal {
class ioport_splitter_device : public ioport_attached_device
{
public:
ioport_splitter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// Next methods are called from the console
void readz(offs_t offset, uint8_t *value) override;
void write(offs_t offset, uint8_t data) override;
void setaddress_dbin(offs_t offset, int state) override;
void crureadz(offs_t offset, uint8_t *value) override;
void cruwrite(offs_t offset, uint8_t data) override;
void memen_in(int state) override;
void msast_in(int state) override;
void clock_in(int state) override;
void reset_in(int state) override;
protected:
ioport_splitter_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_start() override ATTR_COLD;
virtual void device_config_complete() override;
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
// Callbacks from the two ports
template<int port> void extint(int state);
template<int port> void ready(int state);
int m_inta_flag;
int m_ready_flag;
private:
required_device<ioport_device> m_port1;
required_device<ioport_device> m_port2;
};
} // end namespace bus::ti99::internal
DECLARE_DEVICE_TYPE_NS(TI99_IOSPLIT, bus::ti99::internal, ioport_splitter_device)
#endif /* __TI99SPLITTER__ */

View File

@ -216,7 +216,10 @@ CRUCLK* 51||52 DBIN
DEFINE_DEVICE_TYPE(TI99_PERIBOX, bus::ti99::peb::peribox_device, "peribox", "Peripheral expansion box")
// Peripheral box which has a EVPC card in slot 2 (for use with the ti99_4ev)
DEFINE_DEVICE_TYPE(TI99_PERIBOX_EV, bus::ti99::peb::peribox_ev_device, "peribox_ev", "Peripheral expansion box with EVPC")
DEFINE_DEVICE_TYPE(TI99_PERIBOX_EV, bus::ti99::peb::peribox_ev_device, "peribox_ev", "Peripheral expansion box with EVPC inserted")
// Peripheral box with EVPC (for use with the ti99_4ev with the port splitter)
DEFINE_DEVICE_TYPE(TI99_PERIBOX_EV1, bus::ti99::peb::peribox_ev1_device, "peribox_ev1", "Peripheral expansion box with EVPC")
// Peripheral box which hosts the SGCPU card in slot 1
DEFINE_DEVICE_TYPE(TI99_PERIBOX_SG, bus::ti99::peb::peribox_sg_device, "peribox_sg", "Peripheral expansion box SGCPU")
@ -476,31 +479,54 @@ void peribox_device::device_config_complete()
m_ready_flag = 0;
}
void ti99_peribox_slot_standard(device_slot_interface &device)
// Slot options that work with TI-99/4A (with and without EVPC), SGCPU, Geneve,
// and TI-99/8 (untested)
void peribox_common_slots(device_slot_interface &device)
{
device.option_add("32kmem", TI99_32KMEM);
device.option_add("myarcmem", TI99_MYARCMEM);
device.option_add("samsmem", TI99_SAMSMEM);
device.option_add("pcode", TI99_P_CODE);
device.option_add("hsgpl", TI99_HSGPL);
device.option_add("tirs232", TI99_RS232);
device.option_add("speech", TI99_SPEECH);
device.option_add("horizon", TI99_HORIZON);
device.option_add("pgram", TI99_PGRAM);
device.option_add("ide", TI99_IDE);
device.option_add("usbsm", TI99_USBSM);
device.option_add("bwg", TI99_BWG);
device.option_add("hfdc", TI99_HFDC);
device.option_add("tifdc", TI99_FDC);
device.option_add("ccdcc", TI99_CCDCC);
device.option_add("ccfdc", TI99_CCFDC);
device.option_add("ddcc1", TI99_DDCC1);
device.option_add("forti", TI99_FORTI);
device.option_add("sidmaster", TI99_SIDMASTER);
device.option_add("whtscsi", TI99_WHTSCSI);
device.option_add("tipi", TI99_TIPI);
}
// Slot options for TI-99/4A with or without EVPC and SGCPU
// The BwG controller will not run with the Geneve due to its wait state
// logic (see bwg.cpp)
// The SID master card may have trouble with the Geneve because of its CRU
// handling (see sidmaster.cpp)
void peribox_ti99_slots(device_slot_interface &device)
{
device.option_add("myarcmem", TI99_MYARCMEM);
device.option_add("samsmem", TI99_SAMSMEM);
device.option_add("pcode", TI99_P_CODE);
device.option_add("hsgpl", TI99_HSGPL);
device.option_add("bwg", TI99_BWG);
device.option_add("sidmaster", TI99_SIDMASTER);
peribox_common_slots(device);
}
// Geneve and SGCPU do not work with these memory expansions
void peribox_std_slots(device_slot_interface &device)
{
device.option_add("32kmem", TI99_32KMEM);
device.option_add("pgram", TI99_PGRAM);
peribox_ti99_slots(device);
}
void ti99_peribox_slot_standard(device_slot_interface &device)
{
peribox_std_slots(device);
}
void peribox_device::device_add_mconfig(machine_config &config)
{
TI99_PERIBOX_SLOT(config, m_slot2, 2, ti99_peribox_slot_standard, nullptr);
@ -524,28 +550,8 @@ peribox_ev_device::peribox_ev_device(const machine_config &mconfig, const char *
void ti99_peribox_slot_evpc(device_slot_interface &device)
{
device.option_add("evpc", TI99_EVPC);
device.option_add("32kmem", TI99_32KMEM);
device.option_add("myarcmem", TI99_MYARCMEM);
device.option_add("samsmem", TI99_SAMSMEM);
device.option_add("pcode", TI99_P_CODE);
device.option_add("hsgpl", TI99_HSGPL);
device.option_add("tirs232", TI99_RS232);
device.option_add("speech", TI99_SPEECH);
device.option_add("horizon", TI99_HORIZON);
device.option_add("pgram", TI99_PGRAM);
device.option_add("ide", TI99_IDE);
device.option_add("usbsm", TI99_USBSM);
device.option_add("bwg", TI99_BWG);
device.option_add("hfdc", TI99_HFDC);
device.option_add("tifdc", TI99_FDC);
device.option_add("ccdcc", TI99_CCDCC);
device.option_add("ccfdc", TI99_CCFDC);
device.option_add("ddcc1", TI99_DDCC1);
device.option_add("forti", TI99_FORTI);
device.option_add("sidmaster", TI99_SIDMASTER);
device.option_add("whtscsi", TI99_WHTSCSI);
device.option_add("tipi", TI99_TIPI);
device.option_add("evpc", TI99_EVPC);
peribox_std_slots(device);
}
void peribox_ev_device::device_add_mconfig(machine_config &config)
@ -559,6 +565,28 @@ void peribox_ev_device::device_add_mconfig(machine_config &config)
TI99_PERIBOX_SLOT(config, m_slot8, 8, ti99_peribox_slot_evpc, nullptr);
}
/****************************************************************************
A variant of the box used for the TI-99/4A with EVPC, not inserted
*****************************************************************************/
peribox_ev1_device::peribox_ev1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: peribox_device(mconfig, TI99_PERIBOX_EV1, tag, owner, clock)
{
m_address_prefix = 0x70000;
}
void peribox_ev1_device::device_add_mconfig(machine_config &config)
{
TI99_PERIBOX_SLOT(config, m_slot2, 2, ti99_peribox_slot_evpc, nullptr);
TI99_PERIBOX_SLOT(config, m_slot3, 3, ti99_peribox_slot_evpc, nullptr);
TI99_PERIBOX_SLOT(config, m_slot4, 4, ti99_peribox_slot_evpc, nullptr);
TI99_PERIBOX_SLOT(config, m_slot5, 5, ti99_peribox_slot_evpc, nullptr);
TI99_PERIBOX_SLOT(config, m_slot6, 6, ti99_peribox_slot_evpc, nullptr);
TI99_PERIBOX_SLOT(config, m_slot7, 7, ti99_peribox_slot_evpc, nullptr);
TI99_PERIBOX_SLOT(config, m_slot8, 8, ti99_peribox_slot_evpc, nullptr);
}
/****************************************************************************
A variant of the box used for the Geneve.
*****************************************************************************/
@ -582,28 +610,11 @@ peribox_genmod_device::peribox_genmod_device(const machine_config &mconfig, cons
{
}
// The BwG controller will not run with the Geneve due to its wait state
// logic (see bwg.cpp)
// The SID master card may have trouble with the Geneve because of its CRU
// handling (see sidmaster.cpp)
// All common slot options plus Memex.
void ti99_peribox_slot_geneve(device_slot_interface &device)
{
device.option_add("memex", TI99_MEMEX);
device.option_add("tirs232", TI99_RS232);
device.option_add("speech", TI99_SPEECH);
device.option_add("horizon", TI99_HORIZON);
device.option_add("ide", TI99_IDE);
device.option_add("usbsm", TI99_USBSM);
device.option_add("hfdc", TI99_HFDC);
device.option_add("tifdc", TI99_FDC);
device.option_add("ccdcc", TI99_CCDCC);
device.option_add("ccfdc", TI99_CCFDC);
device.option_add("ddcc1", TI99_DDCC1);
device.option_add("forti", TI99_FORTI);
device.option_add("sidmaster", TI99_SIDMASTER);
device.option_add("whtscsi", TI99_WHTSCSI);
device.option_add("tipi", TI99_TIPI);
device.option_add("memex", TI99_MEMEX);
peribox_common_slots(device);
}
void peribox_gen_device::device_add_mconfig(machine_config &config)
@ -642,26 +653,8 @@ peribox_sg_device::peribox_sg_device(const machine_config &mconfig, const char *
void ti99_peribox_slot_sgcpu(device_slot_interface &device)
{
device.option_add("evpc", TI99_EVPC);
device.option_add("myarcmem", TI99_MYARCMEM);
device.option_add("samsmem", TI99_SAMSMEM);
device.option_add("pcode", TI99_P_CODE);
device.option_add("hsgpl", TI99_HSGPL);
device.option_add("tirs232", TI99_RS232);
device.option_add("speech", TI99_SPEECH);
device.option_add("horizon", TI99_HORIZON);
device.option_add("ide", TI99_IDE);
device.option_add("usbsm", TI99_USBSM);
device.option_add("bwg", TI99_BWG);
device.option_add("hfdc", TI99_HFDC);
device.option_add("tifdc", TI99_FDC);
device.option_add("ccdcc", TI99_CCDCC);
device.option_add("ccfdc", TI99_CCFDC);
device.option_add("ddcc1", TI99_DDCC1);
device.option_add("forti", TI99_FORTI);
device.option_add("sidmaster", TI99_SIDMASTER);
device.option_add("whtscsi", TI99_WHTSCSI);
device.option_add("tipi", TI99_TIPI);
device.option_add("evpc", TI99_EVPC);
peribox_ti99_slots(device);
}
void peribox_sg_device::device_add_mconfig(machine_config &config)

View File

@ -140,6 +140,18 @@ protected:
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
};
/*
Variation for ti99_4ev without EVPC inserted
*/
class peribox_ev1_device : public peribox_device
{
public:
peribox_ev1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
protected:
virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
};
/*
Variation for Geneve.
@ -266,6 +278,7 @@ private:
DECLARE_DEVICE_TYPE_NS(TI99_PERIBOX, bus::ti99::peb, peribox_device)
DECLARE_DEVICE_TYPE_NS(TI99_PERIBOX_EV, bus::ti99::peb, peribox_ev_device)
DECLARE_DEVICE_TYPE_NS(TI99_PERIBOX_EV1, bus::ti99::peb, peribox_ev1_device)
DECLARE_DEVICE_TYPE_NS(TI99_PERIBOX_SLOT, bus::ti99::peb, peribox_slot_device)
DECLARE_DEVICE_TYPE_NS(TI99_PERIBOX_SG, bus::ti99::peb, peribox_sg_device)
DECLARE_DEVICE_TYPE_NS(TI99_PERIBOX_GEN, bus::ti99::peb, peribox_gen_device)