// 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) { } void corcomp_fdc_device::setaddress_dbin(offs_t offset, int state) { // 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); } void corcomp_fdc_device::readz(offs_t offset, uint8_t *value) { 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); } } void corcomp_fdc_device::crureadz(offs_t offset, uint8_t *value) { 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); } uint8_t corcomp_fdc_device::tms9901_input(offs_t offset) { // 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(subdevice("0")->subdevices().first()); if (subdevice("1")!=nullptr) m_floppy[1] = static_cast(subdevice("1")->subdevices().first()); if (subdevice("2")!=nullptr) m_floppy[2] = static_cast(subdevice("2")->subdevices().first()); if (subdevice("3")!=nullptr) m_floppy[3] = static_cast(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(subdevice(CCDCC_PALU2_TAG)); m_ctrlpal = static_cast(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(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(owner()); m_decpal = static_cast(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(subdevice(CCFDC_PALU12_TAG)); m_ctrlpal = static_cast(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(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(owner()); m_decpal = static_cast(owner()->subdevice(CCFDC_PALU12_TAG)); } } } } // end namespace bus::ti99::peb