summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99/peb/cc_fdc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/ti99/peb/cc_fdc.cpp')
-rw-r--r--src/devices/bus/ti99/peb/cc_fdc.cpp829
1 files changed, 829 insertions, 0 deletions
diff --git a/src/devices/bus/ti99/peb/cc_fdc.cpp b/src/devices/bus/ti99/peb/cc_fdc.cpp
new file mode 100644
index 00000000000..7fe38b5b4b7
--- /dev/null
+++ b/src/devices/bus/ti99/peb/cc_fdc.cpp
@@ -0,0 +1,829 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/*******************************************************************************
+ CorComp Disk Controller
+ Based on WD2793/WD1773
+ Double Density, Double-sided
+
+ Two flavors:
+
+ * Original controller
+ Named "PEB-DCC"
+ Single 16K EPROM or two 8K EPROMs (selectable by jumper; only 2x8K emulated)
+ Two PALs
+ WD2793
+
+ * Modified controller with redesigned PCB
+ Named "CorComp FDC Rev A"
+ Two 8K EPROMs (by Millers Graphics, 3rd party HW/SW contributor)
+ Two PALs
+ WD1773
+
+ Michael Zapf
+ March 2020
+
+*******************************************************************************/
+
+#include "emu.h"
+#include "cc_fdc.h"
+#include "formats/ti99_dsk.h"
+#include "machine/rescap.h"
+
+// ----------------------------------
+// Flags for debugging
+
+#define LOG_WARN (1U<<1) // Warnings
+#define LOG_CONFIG (1U<<2) // Configuration
+#define LOG_EPROM (1U<<3) // Access to EPROM
+#define LOG_CONTR (1U<<4) // Access to controller
+#define LOG_RAM (1U<<5) // Access to SRAM
+#define LOG_READY (1U<<6) // READY line
+#define LOG_SIGNALS (1U<<7) // IRQ and HLD lines
+#define LOG_DRQ (1U<<8) // DRQ line (too noisy in SIGNALS)
+#define LOG_DRIVE (1U<<9) // Drive operations
+#define LOG_CRU (1U<<10) // CRU operations
+
+#define VERBOSE ( LOG_GENERAL | LOG_WARN | LOG_CONFIG )
+#include "logmacro.h"
+
+#define CCDCC_TAG "ti99_ccdcc"
+#define CCFDC_TAG "ti99_ccfdc"
+#define WDC_TAG "wdfdc"
+#define MOTORMF_TAG "motor_mf"
+#define TMS9901_TAG "tms9901"
+
+#define CCDCC_PALU2_TAG "palu2"
+#define CCDCC_PALU1_TAG "palu1"
+#define CCFDC_PALU12_TAG "palu12"
+#define CCFDC_PALU6_TAG "palu6"
+
+#define BUFFER "ram"
+
+DEFINE_DEVICE_TYPE_NS(TI99_CCDCC, bus::ti99::peb, corcomp_dcc_device, CCDCC_TAG, "CorComp Disk Controller Card")
+DEFINE_DEVICE_TYPE_NS(TI99_CCFDC, bus::ti99::peb, corcomp_fdca_device, CCFDC_TAG, "CorComp Floppy Disk Controller Card Rev A")
+
+DEFINE_DEVICE_TYPE_NS(CCDCC_PALU2, bus::ti99::peb, ccdcc_palu2_device, CCDCC_PALU2_TAG, "CorComp DCC PAL u2")
+DEFINE_DEVICE_TYPE_NS(CCDCC_PALU1, bus::ti99::peb, ccdcc_palu1_device, CCDCC_PALU1_TAG, "CorComp DCC PAL u1")
+
+DEFINE_DEVICE_TYPE_NS(CCFDC_PALU12, bus::ti99::peb, ccfdc_palu12_device, CCFDC_PALU12_TAG, "CorComp FDC PAL u12")
+DEFINE_DEVICE_TYPE_NS(CCFDC_PALU6, bus::ti99::peb, ccfdc_palu6_device, CCFDC_PALU6_TAG, "CorComp FDC PAL u6")
+
+namespace bus { namespace ti99 { namespace peb {
+
+// ----------------------------------
+
+corcomp_fdc_device::corcomp_fdc_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_ti99_peribox_card_interface(mconfig, *this),
+ m_wdc(*this, WDC_TAG),
+ m_decpal(nullptr),
+ m_ctrlpal(nullptr),
+ m_motormf(*this, MOTORMF_TAG),
+ m_tms9901(*this, TMS9901_TAG),
+ m_buffer_ram(*this, BUFFER),
+ m_dsrrom(nullptr),
+ m_cardsel(false),
+ m_banksel(false),
+ m_selected_drive(0),
+ m_address(0),
+ m_writing(false)
+{
+}
+
+SETADDRESS_DBIN_MEMBER( corcomp_fdc_device::setaddress_dbin )
+{
+ // Do not allow setaddress for debugger
+ if (machine().side_effects_disabled()) return;
+ m_address = offset;
+ m_writing = (state==CLEAR_LINE);
+ operate_ready_line();
+}
+
+/*
+ Provides the current address to the PALs.
+*/
+uint16_t corcomp_fdc_device::get_address()
+{
+ return m_address;
+}
+
+/*
+ Before the 9901 configures the P11 pin as output, it delivers Z output,
+ which is pulled down by R10 on the board. We implement this by using a
+ variable.
+*/
+bool corcomp_fdc_device::upper_bank()
+{
+ return m_banksel;
+}
+
+bool corcomp_fdc_device::card_selected()
+{
+ return m_cardsel;
+}
+
+bool corcomp_fdc_device::write_access()
+{
+ return m_writing;
+}
+
+/*
+ The Debugging access must not have any side effect on the controller.
+ We only allow access to the EPROM and RAM.
+*/
+void corcomp_fdc_device::debug_read(offs_t offset, uint8_t* value)
+{
+ uint16_t saveaddress = m_address;
+
+ m_address = offset;
+ *value = 0x00;
+
+ if (m_ctrlpal->selectram())
+ {
+ // SRAM selected
+ *value = m_buffer_ram->pointer()[m_address & 0x7f] & 0xf0; // only the first 4 bits
+ }
+
+ if (m_ctrlpal->selectdsr())
+ {
+ // EPROM selected
+ uint16_t base = m_banksel? 0x2000 : 0;
+ uint8_t* rom = &m_dsrrom[base | (m_address & 0x1fff)];
+ *value = *rom;
+ }
+ m_address = saveaddress;
+}
+
+void corcomp_fdc_device::debug_write(offs_t offset, uint8_t data)
+{
+ uint16_t saveaddress = m_address;
+ m_address = offset;
+ if (m_ctrlpal->selectram())
+ {
+ m_buffer_ram->pointer()[m_address & 0x7f] = data & 0xf0; // only the first 4 bits
+ }
+ m_address = saveaddress;
+}
+
+/*
+ Operate the wait state logic.
+*/
+void corcomp_fdc_device::operate_ready_line()
+{
+ line_state ready = (line_state)m_ctrlpal->ready_out();
+ m_slot->set_ready(ready);
+}
+
+/*
+ Callbacks from the WDC chip
+*/
+WRITE_LINE_MEMBER( corcomp_fdc_device::fdc_irq_w )
+{
+ LOGMASKED(LOG_SIGNALS, "INTRQ callback = %d\n", state);
+ operate_ready_line();
+}
+
+WRITE_LINE_MEMBER( corcomp_fdc_device::fdc_drq_w )
+{
+ LOGMASKED(LOG_DRQ, "DRQ callback = %d\n", state);
+ operate_ready_line();
+}
+
+WRITE_LINE_MEMBER( corcomp_fdc_device::fdc_hld_w )
+{
+ LOGMASKED(LOG_SIGNALS, "HLD callback = %d\n", state);
+}
+
+READ8Z_MEMBER(corcomp_fdc_device::readz)
+{
+ if (machine().side_effects_disabled())
+ {
+ debug_read(offset, value);
+ return;
+ }
+
+ if (m_ctrlpal->selectram())
+ {
+ // SRAM selected
+ *value = m_buffer_ram->pointer()[m_address & 0x7f] & 0xf0; // only the first 4 bits
+ LOGMASKED(LOG_RAM, "Read RAM: %04x -> %02x\n", m_address & 0xffff, *value);
+ }
+
+ if (m_ctrlpal->selectwdc())
+ {
+ // WDC selected
+ *value = m_wdc->read((m_address >> 1)&0x03);
+ LOGMASKED(LOG_CONTR, "Read FDC: %04x -> %02x\n", m_address & 0xffff, *value);
+ }
+
+ if (m_ctrlpal->selectdsr())
+ {
+ // EPROM selected
+ uint16_t base = m_banksel? 0x2000 : 0;
+ uint8_t* rom = &m_dsrrom[base | (m_address & 0x1fff)];
+ *value = *rom;
+
+ if (WORD_ALIGNED(m_address))
+ {
+ uint16_t val = (*rom << 8) | (*(rom+1));
+ LOGMASKED(LOG_EPROM, "Read DSR: %04x (page %d)-> %04x\n", m_address & 0xffff, base>>13, val);
+ }
+ }
+}
+
+void corcomp_fdc_device::write(offs_t offset, uint8_t data)
+{
+ if (machine().side_effects_disabled())
+ {
+ debug_write(offset, data);
+ return;
+ }
+ if (m_ctrlpal->selectram())
+ {
+ // SRAM selected
+ LOGMASKED(LOG_RAM, "Write RAM: %04x <- %02x\n", m_address & 0xffff, data&0xf0);
+ m_buffer_ram->pointer()[m_address & 0x7f] = data & 0xf0; // only the first 4 bits
+ }
+
+ if (m_ctrlpal->selectwdc())
+ {
+ // WDC selected
+ LOGMASKED(LOG_CONTR, "Write FDC: %04x <- %02x\n", m_address & 0xffff, data);
+ m_wdc->write((m_address >> 1)&0x03, data);
+ }
+}
+
+READ8Z_MEMBER( corcomp_fdc_device::crureadz )
+{
+ m_address = offset; // Copy the CRU address on the address variable
+ if (m_decpal->address9901())
+ {
+ // The S0 select line is inverted, which means that
+ // a CRU address of 0x1120 is actually address 0x00 for the 9901,
+ // while the CRU address 0x1100 is 0x20 for the 9901.
+ // This trick is necessary to relocate the P0 line to the
+ // first CRU address, which by convention turns on the EPROM
+ // of the card.
+ int bitno = ((offset & 0x3e)^0x20)>>1;
+ *value = m_tms9901->read_bit(bitno)? 0x01 : 0x00;
+ LOGMASKED(LOG_CRU, "cru %04x (bit %d) -> %d\n", offset, bitno, *value);
+ }
+}
+
+void corcomp_fdc_device::cruwrite(offs_t offset, uint8_t data)
+{
+ m_address = offset; // Copy the CRU address on the address variable
+ if (m_decpal->address9901())
+ {
+ int bitno = ((offset & 0x3e)^0x20)>>1;
+ LOGMASKED(LOG_CRU, "cru %04x (bit %d) <- %d\n", offset, bitno, data);
+ m_tms9901->write_bit(bitno, data!=0);
+ }
+}
+
+WRITE_LINE_MEMBER( corcomp_fdc_device::clock_in )
+{
+ m_tms9901->phi_line(state);
+}
+
+READ8_MEMBER( corcomp_fdc_device::tms9901_input )
+{
+ // Inputs
+ // INT1: Switch 8
+ // INT2: Switch 1
+ // INT3: Switch 7
+ // INT4: Switch 5
+ // INT5: Switch 3
+ // INT6: Switch 2
+ // INT7: Switch 4
+ // INT8: Switch 6
+ // INT9: -
+ // P9: MotorMF
+ // P12: WDCu11.28 (HLD)
+ const uint8_t dipswitch[] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+ switch (offset)
+ {
+ case tms9901_device::INT1:
+ return ((ioport("HEADSTEP")->read() & dipswitch[8])!=0)? 0:1;
+ case tms9901_device::INT2:
+ return ((ioport("HEADSTEP")->read() & dipswitch[1])!=0)? 0:1;
+ case tms9901_device::INT3:
+ return ((ioport("HEADSTEP")->read() & dipswitch[7])!=0)? 0:1;
+ case tms9901_device::INT4:
+ return ((ioport("HEADSTEP")->read() & dipswitch[5])!=0)? 0:1;
+ case tms9901_device::INT5:
+ return ((ioport("HEADSTEP")->read() & dipswitch[3])!=0)? 0:1;
+ case tms9901_device::INT6:
+ return ((ioport("HEADSTEP")->read() & dipswitch[2])!=0)? 0:1;
+ case tms9901_device::INT7_P15:
+ return ((ioport("HEADSTEP")->read() & dipswitch[4])!=0)? 0:1;
+ case tms9901_device::INT8_P14:
+ return ((ioport("HEADSTEP")->read() & dipswitch[6])!=0)? 0:1;
+ case tms9901_device::INT10_P12:
+ return (m_wdc->hld_r()==ASSERT_LINE)? 1:0;
+ case tms9901_device::INT13_P9:
+ return (m_motormf->q_r()==0)? 1:0;
+ default:
+ return 1;
+ }
+}
+
+WRITE_LINE_MEMBER( corcomp_fdc_device::select_dsk )
+{
+ if (state == CLEAR_LINE)
+ {
+ if ( (!m_tms9901->read_bit(tms9901_device::P4))
+ && (!m_tms9901->read_bit(tms9901_device::P5))
+ && (!m_tms9901->read_bit(tms9901_device::P6))
+ && (!m_tms9901->read_bit(tms9901_device::INT14_P8)))
+ {
+ LOGMASKED(LOG_DRIVE, "Unselect all drives\n");
+ m_wdc->set_floppy(nullptr);
+ m_selected_drive = 0;
+ }
+ }
+ else
+ {
+ if (m_tms9901->read_bit(tms9901_device::P4))
+ {
+ m_selected_drive = 1;
+ }
+ else if (m_tms9901->read_bit(tms9901_device::P5))
+ {
+ m_selected_drive = 2;
+ }
+ else if (m_tms9901->read_bit(tms9901_device::P6))
+ {
+ m_selected_drive = 3;
+ }
+ else if (m_tms9901->read_bit(tms9901_device::INT14_P8))
+ {
+ m_selected_drive = 4;
+ }
+ LOGMASKED(LOG_DRIVE, "Select drive DSK%d\n", m_selected_drive);
+
+ if (m_floppy[m_selected_drive-1] != nullptr)
+ {
+ m_wdc->set_floppy(m_floppy[m_selected_drive-1]);
+ m_floppy[m_selected_drive-1]->ss_w(m_tms9901->read_bit(tms9901_device::INT15_P7));
+ }
+ }
+}
+
+WRITE_LINE_MEMBER( corcomp_fdc_device::side_select )
+{
+ // Select side of disk (bit 7)
+ if (m_selected_drive != 0)
+ {
+ LOGMASKED(LOG_DRIVE, "Set side (bit 7) = %d on DSK%d\n", state, m_selected_drive);
+ m_floppy[m_selected_drive-1]->ss_w(state);
+ }
+}
+
+/*
+ All floppy motors are operated by the same line.
+*/
+WRITE_LINE_MEMBER( corcomp_fdc_device::motor_w )
+{
+ LOGMASKED(LOG_DRIVE, "Motor %s\n", state? "on" : "off");
+ m_wdc->set_force_ready(state==ASSERT_LINE);
+
+ // Set all motors
+ for (auto & elem : m_floppy)
+ if (elem != nullptr) elem->mon_w((state==ASSERT_LINE)? 0 : 1);
+ operate_ready_line();
+}
+
+/*
+ Push the P11 state to the variable.
+*/
+WRITE_LINE_MEMBER( corcomp_fdc_device::select_bank )
+{
+ LOGMASKED(LOG_CRU, "Set bank %d\n", state);
+ m_banksel = (state==ASSERT_LINE);
+ operate_ready_line();
+}
+
+WRITE_LINE_MEMBER( corcomp_fdc_device::select_card )
+{
+ LOGMASKED(LOG_CRU, "Select card = %d\n", state);
+ m_cardsel = (state==ASSERT_LINE);
+ operate_ready_line();
+}
+
+// =========================================================================
+
+void corcomp_fdc_device::device_start()
+{
+ m_dsrrom = memregion(TI99_DSRROM)->base();
+ save_item(NAME(m_address));
+ save_item(NAME(m_writing));
+ save_item(NAME(m_cardsel));
+ save_item(NAME(m_banksel));
+ save_item(NAME(m_selected_drive));
+}
+
+void corcomp_fdc_device::device_reset()
+{
+ for (int i=0; i < 4; i++)
+ {
+ if (m_floppy[i] != nullptr)
+ LOGMASKED(LOG_CONFIG, "Connector %d with %s\n", i, m_floppy[i]->name());
+ else
+ LOGMASKED(LOG_CONFIG, "Connector %d has no floppy attached\n", i);
+ }
+}
+
+void corcomp_fdc_device::connect_drives()
+{
+ for (auto & elem : m_floppy)
+ elem = nullptr;
+
+ if (subdevice("0")!=nullptr) m_floppy[0] = static_cast<floppy_image_device*>(subdevice("0")->subdevices().first());
+ if (subdevice("1")!=nullptr) m_floppy[1] = static_cast<floppy_image_device*>(subdevice("1")->subdevices().first());
+ if (subdevice("2")!=nullptr) m_floppy[2] = static_cast<floppy_image_device*>(subdevice("2")->subdevices().first());
+ if (subdevice("3")!=nullptr) m_floppy[3] = static_cast<floppy_image_device*>(subdevice("3")->subdevices().first());
+}
+
+INPUT_PORTS_START( cc_fdc )
+ PORT_START( "HEADSTEP" )
+ PORT_DIPNAME( 0x03, 0x00, "DSK1 head step time" )
+ PORT_DIPSETTING( 0x00, "15 ms")
+ PORT_DIPSETTING( 0x01, "10 ms")
+ PORT_DIPSETTING( 0x02, "6 ms")
+ PORT_DIPSETTING( 0x03, "3 ms")
+ PORT_DIPNAME( 0x0c, 0x00, "DSK2 head step time" )
+ PORT_DIPSETTING( 0x00, "15 ms")
+ PORT_DIPSETTING( 0x04, "10 ms")
+ PORT_DIPSETTING( 0x08, "6 ms")
+ PORT_DIPSETTING( 0x0c, "3 ms")
+ PORT_DIPNAME( 0x30, 0x00, "DSK3 head step time" )
+ PORT_DIPSETTING( 0x00, "15 ms")
+ PORT_DIPSETTING( 0x10, "10 ms")
+ PORT_DIPSETTING( 0x20, "6 ms")
+ PORT_DIPSETTING( 0x30, "3 ms")
+ PORT_DIPNAME( 0xc0, 0x00, "DSK4 head step time" )
+ PORT_DIPSETTING( 0x00, "15 ms")
+ PORT_DIPSETTING( 0x40, "10 ms")
+ PORT_DIPSETTING( 0x80, "6 ms")
+ PORT_DIPSETTING( 0xc0, "3 ms")
+INPUT_PORTS_END
+
+FLOPPY_FORMATS_MEMBER(corcomp_fdc_device::floppy_formats)
+ FLOPPY_TI99_SDF_FORMAT,
+ FLOPPY_TI99_TDF_FORMAT
+FLOPPY_FORMATS_END
+
+static void ccfdc_floppies(device_slot_interface &device)
+{
+ device.option_add("525dd", FLOPPY_525_DD); // 40 tracks
+ device.option_add("525qd", FLOPPY_525_QD); // 80 tracks
+ device.option_add("35dd", FLOPPY_35_DD); // 80 tracks
+}
+
+void corcomp_fdc_device::common_config(machine_config& config)
+{
+ m_wdc->intrq_wr_callback().set(FUNC(corcomp_fdc_device::fdc_irq_w));
+ m_wdc->drq_wr_callback().set(FUNC(corcomp_fdc_device::fdc_drq_w));
+ m_wdc->hld_wr_callback().set(FUNC(corcomp_fdc_device::fdc_hld_w));
+
+ TMS9901(config, m_tms9901, 0);
+ m_tms9901->read_cb().set(FUNC(corcomp_fdc_device::tms9901_input));
+
+ // Outputs
+ // P0: LED (DSR?), PALu2.1
+ // P1: MFu6.clk (Motor)
+ // P2: PALu1.5 (WATEN)
+ // P3: WDCu11.50 (HLT)
+ // P4: DSK1 select
+ // P5: DSK2 select
+ // P6: DSK3 select
+ // P7: SIDSEL
+ // P8: DSK4 select
+ // P10: WDCu11.37 (DDEN)
+ // P11: ROMBNK
+ // P13: -
+ // P14: -
+ // P15: -
+ m_tms9901->p_out_cb(0).set(FUNC(corcomp_fdc_device::select_card));
+ m_tms9901->p_out_cb(1).set(MOTORMF_TAG, FUNC(ttl74123_device::b_w));
+ m_tms9901->p_out_cb(4).set(FUNC(corcomp_fdc_device::select_dsk));
+ m_tms9901->p_out_cb(5).set(FUNC(corcomp_fdc_device::select_dsk));
+ m_tms9901->p_out_cb(6).set(FUNC(corcomp_fdc_device::select_dsk));
+ m_tms9901->p_out_cb(7).set(FUNC(corcomp_fdc_device::side_select));
+ m_tms9901->p_out_cb(8).set(FUNC(corcomp_fdc_device::select_dsk));
+ m_tms9901->p_out_cb(10).set(WDC_TAG, FUNC(wd_fdc_device_base::dden_w));
+ m_tms9901->p_out_cb(11).set(FUNC(corcomp_fdc_device::select_bank));
+
+ // Motor monoflop
+ TTL74123(config, m_motormf, 0);
+ m_motormf->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
+ m_motormf->set_resistor_value(RES_K(100));
+ m_motormf->set_capacitor_value(CAP_U(47));
+ m_motormf->set_a_pin_value(0);
+ m_motormf->set_b_pin_value(1);
+ m_motormf->set_clear_pin_value(1);
+ m_motormf->out_cb().set(FUNC(corcomp_fdc_device::motor_w));
+
+ FLOPPY_CONNECTOR(config, "0", ccfdc_floppies, "525dd", corcomp_fdc_device::floppy_formats).enable_sound(true);
+ FLOPPY_CONNECTOR(config, "1", ccfdc_floppies, "525dd", corcomp_fdc_device::floppy_formats).enable_sound(true);
+ FLOPPY_CONNECTOR(config, "2", ccfdc_floppies, nullptr, corcomp_fdc_device::floppy_formats).enable_sound(true);
+ FLOPPY_CONNECTOR(config, "3", ccfdc_floppies, nullptr, corcomp_fdc_device::floppy_formats).enable_sound(true);
+
+ // SRAM 2114 1Kx4
+ RAM(config, BUFFER).set_default_size("1k").set_default_value(0);
+}
+
+// ============================================================================
+// Original CorComp Disk Controller Card (PEB-DCC)
+// ============================================================================
+
+corcomp_dcc_device::corcomp_dcc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
+ corcomp_fdc_device(mconfig, TI99_CCDCC, tag, owner, clock)
+{
+}
+
+void corcomp_dcc_device::device_add_mconfig(machine_config& config)
+{
+ WD2793(config, m_wdc, 4_MHz_XTAL / 4);
+ common_config(config);
+
+ // For the 2793, attach the HLT line
+ m_tms9901->p_out_cb(3).set(WDC_TAG, FUNC(wd_fdc_device_base::hlt_w));
+
+ // PAL circuits are connected in device_config_complete
+ CCDCC_PALU2(config, CCDCC_PALU2_TAG, 0);
+ CCDCC_PALU1(config, CCDCC_PALU1_TAG, 0);
+}
+
+ROM_START( cc_dcc )
+ ROM_REGION(0x4000, TI99_DSRROM, 0)
+ ROM_LOAD("ccdcc_v89.u3", 0x0000, 0x2000, CRC(de3f9476) SHA1(b88aea1141769dad4e4bea5f93ac4f63a627cc82)) /* 8K single ROM bank 1*/
+ ROM_LOAD("ccdcc_v89.u4", 0x2000, 0x2000, CRC(9c4e5c08) SHA1(26f8096ae60f3839902b4e8764c5fde283ad4ba2)) /* 8K single ROM bank 2*/
+ROM_END
+
+void corcomp_dcc_device::device_config_complete()
+{
+ m_decpal = static_cast<ccfdc_dec_pal_device*>(subdevice(CCDCC_PALU2_TAG));
+ m_ctrlpal = static_cast<ccfdc_sel_pal_device*>(subdevice(CCDCC_PALU1_TAG));
+ connect_drives();
+}
+
+ioport_constructor corcomp_fdc_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME( cc_fdc );
+}
+
+const tiny_rom_entry *corcomp_dcc_device::device_rom_region() const
+{
+ return ROM_NAME( cc_dcc );
+}
+
+// ========================================================================
+// PAL circuits on the CorComp board
+// ========================================================================
+
+ccfdc_dec_pal_device::ccfdc_dec_pal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
+ m_board(nullptr),
+ m_tms9901(*owner, TMS9901_TAG)
+{
+}
+
+ccfdc_sel_pal_device::ccfdc_sel_pal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, type, tag, owner, clock),
+ m_board(nullptr),
+ m_decpal(nullptr),
+ m_motormf(*owner, MOTORMF_TAG),
+ m_tms9901(*owner, TMS9901_TAG),
+ m_wdc(*owner, WDC_TAG)
+{
+}
+
+void ccfdc_dec_pal_device::device_config_complete()
+{
+ m_board = static_cast<corcomp_fdc_device*>(owner());
+}
+
+/*
+ Indicates 9901 addressing.
+*/
+READ_LINE_MEMBER( ccfdc_dec_pal_device::address9901 )
+{
+ return ((m_board->get_address() & 0xff80)==0x1100)? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates SRAM addressing.
+*/
+READ_LINE_MEMBER( ccfdc_dec_pal_device::addressram )
+{
+ return ((m_board->card_selected()) &&
+ (m_board->get_address() & 0xff80)==0x4000)? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates WDC addressing.
+*/
+READ_LINE_MEMBER( ccfdc_dec_pal_device::addresswdc )
+{
+ return ((m_board->card_selected()) &&
+ (m_board->get_address() & 0xff80)==0x5f80)? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates DSR addressing.
+*/
+READ_LINE_MEMBER( ccfdc_dec_pal_device::address4 )
+{
+ return ((m_board->card_selected()) &&
+ (m_board->get_address() & 0xe000)==0x4000)? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates SRAM selection.
+*/
+READ_LINE_MEMBER( ccfdc_sel_pal_device::selectram )
+{
+ return (m_decpal->addressram() && (m_board->upper_bank()))
+ ? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates WDC selection.
+*/
+READ_LINE_MEMBER( ccfdc_sel_pal_device::selectwdc )
+{
+ return (m_decpal->addresswdc() && ((m_board->get_address()&1)==0))? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates EPROM selection.
+*/
+READ_LINE_MEMBER( ccfdc_sel_pal_device::selectdsr )
+{
+ return (m_decpal->address4()
+ && !m_decpal->addresswdc()
+ && !(m_decpal->addressram() && (m_board->upper_bank())))
+ ? ASSERT_LINE : CLEAR_LINE;
+}
+
+// ========================================================================
+// PAL circuits on the original CorComp board
+// PAL u2 is the address decoder, delivering its results to the
+// selector PAL u1.
+// ========================================================================
+
+ccdcc_palu2_device::ccdcc_palu2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : ccfdc_dec_pal_device(mconfig, CCDCC_PALU2, tag, owner, clock)
+{
+}
+
+ccdcc_palu1_device::ccdcc_palu1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : ccfdc_sel_pal_device(mconfig, CCDCC_PALU1, tag, owner, clock)
+{
+}
+
+/*
+ Wait state logic
+*/
+READ_LINE_MEMBER( ccdcc_palu1_device::ready_out )
+{
+ bool wdc = m_decpal->addresswdc(); // Addressing the WDC
+ bool lastdig = (m_board->get_address()&7)==6; // Address ends with 6 or e (5ff6, 5ffe)
+ bool trap = m_tms9901->read_bit(tms9901_device::P2); // Wait state generation is active (SBO 2)
+ bool waitbyte = m_wdc->drq_r()==CLEAR_LINE; // We are waiting for a byte
+ bool noterm = m_wdc->intrq_r()==CLEAR_LINE; // There is no interrupt yet
+ bool motor = (m_motormf->q_r()==1); // The disk is spinning
+
+ line_state ready = (wdc && lastdig && trap && waitbyte && noterm && motor)? CLEAR_LINE : ASSERT_LINE; // then clear READY and thus trigger wait states
+
+ LOGMASKED(LOG_READY, "READY = %d (%d,%d,%d,%d,%d,%d)\n", ready, wdc, lastdig, trap, waitbyte, noterm, motor);
+ return ready;
+}
+
+void ccdcc_palu1_device::device_config_complete()
+{
+ m_board = static_cast<corcomp_fdc_device*>(owner());
+ m_decpal = static_cast<ccfdc_dec_pal_device*>(owner()->subdevice(CCDCC_PALU2_TAG));
+}
+
+// ============================================================================
+// Revised CorComp floppy disk controller card REV A
+// ============================================================================
+
+corcomp_fdca_device::corcomp_fdca_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
+ corcomp_fdc_device(mconfig, TI99_CCFDC, tag, owner, clock)
+{
+}
+
+void corcomp_fdca_device::device_add_mconfig(machine_config& config)
+{
+ WD1773(config, m_wdc, 8_MHz_XTAL);
+ common_config(config);
+
+ // PAL circuits are connected in device_config_complete
+ CCFDC_PALU12(config, CCFDC_PALU12_TAG, 0);
+ CCFDC_PALU6(config, CCFDC_PALU6_TAG, 0);
+}
+
+/*
+ READY trap circuitry on the revised board (U10)
+*/
+bool corcomp_fdca_device::ready_trap_active()
+{
+ return m_tms9901->read_bit(tms9901_device::P2)
+ && ((m_address & 6)==6)
+ && (m_motormf->q_r()==1);
+}
+
+ROM_START( cc_fdcmg )
+ ROM_REGION(0x8000, TI99_DSRROM, 0)
+ ROM_LOAD("ccfdc_v89mg.u1", 0x0000, 0x2000, CRC(f010e273) SHA1(bd30103d80c43d4b35e0669145cef7b5c6b9813b)) /* 16K single ROM */
+ ROM_LOAD("ccfdc_v89mg.u2", 0x2000, 0x2000, CRC(0cad8f5b) SHA1(7744f777b51eedf614f766576bbc3f8c2c2e0042)) /* 16K single ROM */
+ROM_END
+
+void corcomp_fdca_device::device_config_complete()
+{
+ m_decpal = static_cast<ccfdc_dec_pal_device*>(subdevice(CCFDC_PALU12_TAG));
+ m_ctrlpal = static_cast<ccfdc_sel_pal_device*>(subdevice(CCFDC_PALU6_TAG));
+ connect_drives();
+}
+
+const tiny_rom_entry *corcomp_fdca_device::device_rom_region() const
+{
+ return ROM_NAME( cc_fdcmg );
+}
+
+// ========================================================================
+// PAL circuits on the revised CorComp board
+// PAL u12 is the address decoder, delivering its results to the
+// selector PAL u6.
+// ========================================================================
+
+ccfdc_palu12_device::ccfdc_palu12_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : ccfdc_dec_pal_device(mconfig, CCFDC_PALU12, tag, owner, clock)
+{
+}
+
+/*
+ Indicates 9901 addressing. In this PAL version, the A9 address line is
+ also used.
+*/
+READ_LINE_MEMBER( ccfdc_palu12_device::address9901 )
+{
+ return ((m_board->get_address() & 0xffc0)==0x1100)? ASSERT_LINE : CLEAR_LINE;
+}
+
+ccfdc_palu6_device::ccfdc_palu6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : ccfdc_sel_pal_device(mconfig, CCFDC_PALU6, tag, owner, clock)
+{
+}
+
+/*
+ Indicates WDC selection. Also checks whether A12 and /WE match.
+ That is, when writing (/WE=0), A12 must be 1 (addresses 5ff8..e),
+ otherwise (/WE=1), A12 must be 0 (addresses 5ff0..6)
+*/
+READ_LINE_MEMBER( ccfdc_palu6_device::selectwdc )
+{
+ return (m_decpal->addresswdc()
+ && ((m_board->get_address()&1)==0)
+ && (((m_board->get_address()&8)!=0)==(m_board->write_access())))? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Indicates EPROM selection. The Rev A selector PAL leads back some of
+ its outputs for this calculation.
+*/
+READ_LINE_MEMBER( ccfdc_palu6_device::selectdsr )
+{
+ return (m_decpal->address4() && !selectwdc() && !selectram())? ASSERT_LINE : CLEAR_LINE;
+}
+
+/*
+ Wait state logic. The Rev A selector relies on an AND circuit on the
+ board which evaluates whether the trap is active.
+*/
+
+READ_LINE_MEMBER( ccfdc_palu6_device::ready_out )
+{
+ bool wdc = m_decpal->addresswdc(); // Addressing the WDC
+ bool even = (m_board->get_address()&1)==0; // A15 = 0
+ bool trap = static_cast<corcomp_fdca_device*>(m_board)->ready_trap_active(); // READY trap active
+ bool waitbyte = m_wdc->drq_r()==CLEAR_LINE; // We are waiting for a byte
+ bool noterm = m_wdc->intrq_r()==CLEAR_LINE; // There is no interrupt yet
+
+ line_state ready = (wdc && even && trap && waitbyte && noterm)? CLEAR_LINE : ASSERT_LINE; // then clear READY and thus trigger wait states
+
+ LOGMASKED(LOG_READY, "READY = %d (%d,%d,%d,%d,%d)\n", ready, wdc, even, trap, waitbyte, noterm);
+ return ready;
+}
+
+void ccfdc_palu6_device::device_config_complete()
+{
+ m_board = static_cast<corcomp_fdca_device*>(owner());
+ m_decpal = static_cast<ccfdc_dec_pal_device*>(owner()->subdevice(CCFDC_PALU12_TAG));
+}
+} } } // end namespace bus::ti99::peb