From 91b633c3639b2754e82b590747a6c37e64bc8898 Mon Sep 17 00:00:00 2001 From: Michael Zapf Date: Sat, 14 Mar 2020 01:26:49 +0100 Subject: [PATCH] ti99: Added CorComp disk controller cards. --- scripts/src/bus.lua | 2 + src/devices/bus/ti99/gromport/cartridges.cpp | 4 +- src/devices/bus/ti99/internal/genboard.cpp | 4 +- src/devices/bus/ti99/peb/cc_fdc.cpp | 829 +++++++++++++++++++ src/devices/bus/ti99/peb/cc_fdc.h | 252 ++++++ src/devices/bus/ti99/peb/peribox.cpp | 9 + src/devices/machine/tms9901.cpp | 25 +- src/devices/machine/tms9901.h | 3 + 8 files changed, 1116 insertions(+), 12 deletions(-) create mode 100644 src/devices/bus/ti99/peb/cc_fdc.cpp create mode 100644 src/devices/bus/ti99/peb/cc_fdc.h diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 0a8d3565b18..b4a4b66a6b8 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -3142,6 +3142,8 @@ if (BUSES["TI99"]~=null) then MAME_DIR .. "src/devices/bus/ti99/peb/peribox.h", MAME_DIR .. "src/devices/bus/ti99/peb/bwg.cpp", MAME_DIR .. "src/devices/bus/ti99/peb/bwg.h", + MAME_DIR .. "src/devices/bus/ti99/peb/cc_fdc.cpp", + MAME_DIR .. "src/devices/bus/ti99/peb/cc_fdc.h", MAME_DIR .. "src/devices/bus/ti99/peb/evpc.cpp", MAME_DIR .. "src/devices/bus/ti99/peb/evpc.h", MAME_DIR .. "src/devices/bus/ti99/peb/hfdc.cpp", diff --git a/src/devices/bus/ti99/gromport/cartridges.cpp b/src/devices/bus/ti99/gromport/cartridges.cpp index b56e3dbb4f9..397522ea07f 100644 --- a/src/devices/bus/ti99/gromport/cartridges.cpp +++ b/src/devices/bus/ti99/gromport/cartridges.cpp @@ -25,7 +25,7 @@ #define LOG_GROM (1U<<8) // GROM access #define LOG_RPK (1U<<9) // RPK handler -#define VERBOSE ( LOG_WARN ) +#define VERBOSE ( LOG_GENERAL | LOG_WARN ) #include "logmacro.h" DEFINE_DEVICE_TYPE_NS(TI99_CART, bus::ti99::gromport, ti99_cartridge_device, "ti99cart", "TI-99 cartridge") @@ -634,7 +634,7 @@ void ti99_paged12k_cartridge::write(offs_t offset, uint8_t data) { m_rom_page = (offset >> 1) & 1; if ((offset & 1)==0) - LOGMASKED(LOG_WARN, "Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); + LOGMASKED(LOG_BANKSWITCH, "Set ROM page = %d (writing to %04x)\n", m_rom_page, (offset | 0x6000)); } else { diff --git a/src/devices/bus/ti99/internal/genboard.cpp b/src/devices/bus/ti99/internal/genboard.cpp index 2864d23d834..81b28a17d39 100644 --- a/src/devices/bus/ti99/internal/genboard.cpp +++ b/src/devices/bus/ti99/internal/genboard.cpp @@ -429,7 +429,7 @@ #define LOG_MAPPER (1U<<16) // Minimum log should be warnings -#define VERBOSE ( LOG_GENERAL | LOG_SETTING | LOG_WARN ) +#define VERBOSE ( LOG_GENERAL | LOG_WARN ) #include "genboard.h" #include "logmacro.h" @@ -527,7 +527,7 @@ WRITE8_MEMBER( geneve_gate_array_device::cru_ctrl_write ) m_cartridge7_writable = (data!=0); break; case 15: - LOGMASKED(LOG_SETTING, "Extra wait states %s\n", (data==0)? "enabled" : "disabled"); + LOGMASKED(LOG_CRU, "Extra wait states %s\n", (data==0)? "enabled" : "disabled"); m_enable_extra_waitstates = (data==0); break; default: 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(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 diff --git a/src/devices/bus/ti99/peb/cc_fdc.h b/src/devices/bus/ti99/peb/cc_fdc.h new file mode 100644 index 00000000000..ac356c8c0c7 --- /dev/null +++ b/src/devices/bus/ti99/peb/cc_fdc.h @@ -0,0 +1,252 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + Corcomp Disk Controller + Based on WD2793/WD1773 + Double Density, Double-sided + + Michael Zapf, March 2020 + +*****************************************************************************/ + +#ifndef MAME_BUS_TI99_PEB_CORCOMP_H +#define MAME_BUS_TI99_PEB_CORCOMP_H + +#pragma once + +#include "peribox.h" +#include "imagedev/floppy.h" +#include "machine/wd_fdc.h" +#include "machine/74123.h" +#include "machine/tms9901.h" +#include "machine/ram.h" + +namespace bus { namespace ti99 { namespace peb { + +class ccfdc_dec_pal_device; +class ccfdc_sel_pal_device; +class ccfdc_palu12_device; +class ccfdc_palu6_device; + +class corcomp_fdc_device : public device_t, public device_ti99_peribox_card_interface +{ + friend class ccfdc_dec_pal_device; + friend class ccfdc_sel_pal_device; + friend class ccdcc_palu1_device; + friend class ccfdc_palu6_device; + friend class ccfdc_palu12_device; + +public: + DECLARE_READ8Z_MEMBER(readz) override; + void write(offs_t offset, uint8_t data) override; + DECLARE_SETADDRESS_DBIN_MEMBER(setaddress_dbin) override; + + DECLARE_READ8Z_MEMBER(crureadz) override; + void cruwrite(offs_t offset, uint8_t data) override; + DECLARE_WRITE_LINE_MEMBER(clock_in) override; + + DECLARE_WRITE_LINE_MEMBER( fdc_irq_w ); + DECLARE_WRITE_LINE_MEMBER( fdc_drq_w ); + DECLARE_WRITE_LINE_MEMBER( fdc_hld_w ); + DECLARE_READ8_MEMBER( tms9901_input ); + DECLARE_WRITE_LINE_MEMBER( select_dsk ); + DECLARE_WRITE_LINE_MEMBER( side_select ); + DECLARE_WRITE_LINE_MEMBER( motor_w ); + DECLARE_WRITE_LINE_MEMBER( select_card ); + DECLARE_WRITE_LINE_MEMBER( select_bank ); + +protected: + corcomp_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + void device_start() override; + void device_reset() override; + void connect_drives(); + + virtual void device_add_mconfig(machine_config &config) override =0; + ioport_constructor device_input_ports() const override; + + void common_config(machine_config& config); + + DECLARE_FLOPPY_FORMATS( floppy_formats ); + + // Link to the WD controller on the board. + required_device m_wdc; + + // PALs + ccfdc_dec_pal_device* m_decpal; + ccfdc_sel_pal_device* m_ctrlpal; + + // Lines that are polled by the PAL chips + bool card_selected(); + bool upper_bank(); + bool write_access(); + bool ready_trap_active(); + + // Deliver the current state of the address bus + uint16_t get_address(); + + // Wait state logic + void operate_ready_line(); + + // Link to the attached floppy drives + floppy_image_device* m_floppy[4]; + + // Motor monoflop + required_device m_motormf; + + // Interface chip + required_device m_tms9901; + + // Debugger accessors + void debug_read(offs_t offset, uint8_t* value); + void debug_write(offs_t offset, uint8_t data); + + // Buffer RAM + required_device m_buffer_ram; + + // DSR ROM + uint8_t* m_dsrrom; + + // State of the bank select line. Pulled down initially. + bool m_cardsel; + bool m_banksel; + + // Selected drive + int m_selected_drive; + + // Recent address + int m_address; + + // Write access + bool m_writing; +}; + + +// ============================================================================ +// Original CorComp disk controller card +// ============================================================================ + +class corcomp_dcc_device : public corcomp_fdc_device +{ +public: + corcomp_dcc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +private: + void device_add_mconfig(machine_config &config) override; + void device_config_complete() override; + const tiny_rom_entry *device_rom_region() const override; +}; + +// =========== Decoder PAL circuit ================ +class ccfdc_dec_pal_device : public device_t +{ +public: + DECLARE_READ_LINE_MEMBER(addresswdc); + DECLARE_READ_LINE_MEMBER(address4); + DECLARE_READ_LINE_MEMBER(addressram); + virtual DECLARE_READ_LINE_MEMBER(address9901); + +protected: + ccfdc_dec_pal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + void device_start() override { }; + void device_config_complete() override; + + corcomp_fdc_device* m_board; + required_device m_tms9901; +}; + +// =========== Selector PAL circuit ================ + +class ccfdc_sel_pal_device : public device_t +{ +public: + DECLARE_READ_LINE_MEMBER(selectram); + virtual DECLARE_READ_LINE_MEMBER(selectwdc); + virtual DECLARE_READ_LINE_MEMBER(selectdsr); + virtual DECLARE_READ_LINE_MEMBER(ready_out) =0; + +protected: + ccfdc_sel_pal_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + void device_start() override { }; + virtual void device_config_complete() override =0; + + corcomp_fdc_device* m_board; + ccfdc_dec_pal_device* m_decpal; + required_device m_motormf; + required_device m_tms9901; + required_device m_wdc; +}; + +// =========== Specific decoder PAL circuit of the CCDCC ================ + +class ccdcc_palu2_device : public ccfdc_dec_pal_device +{ +public: + ccdcc_palu2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +}; + +// =========== Specific selector PAL circuit of the CCDCC ================ + +class ccdcc_palu1_device : public ccfdc_sel_pal_device +{ +public: + ccdcc_palu1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ_LINE_MEMBER(ready_out) override; + +private: + void device_config_complete() override; +}; + +// ============================================================================ +// Revised CorComp floppy disk controller card rev a +// ============================================================================ + +class corcomp_fdca_device : public corcomp_fdc_device +{ + friend class ccfdc_palu6_device; + +public: + corcomp_fdca_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); +private: + void device_add_mconfig(machine_config &config) override; + void device_config_complete() override; + const tiny_rom_entry *device_rom_region() const override; + bool ready_trap_active(); +}; + +// =========== Specific decoder PAL circuit of the CCFDC ================ + +class ccfdc_palu12_device : public ccfdc_dec_pal_device +{ +public: + ccfdc_palu12_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ_LINE_MEMBER(address9901) override; +}; + +// =========== Specific selector PAL circuit of the CCFDC ================ + +class ccfdc_palu6_device : public ccfdc_sel_pal_device +{ +public: + ccfdc_palu6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + DECLARE_READ_LINE_MEMBER(selectwdc) override; + DECLARE_READ_LINE_MEMBER(selectdsr) override; + + DECLARE_READ_LINE_MEMBER(ready_out) override; + +private: + void device_config_complete() override; +}; + +} } } // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_CCDCC, bus::ti99::peb, corcomp_dcc_device) +DECLARE_DEVICE_TYPE_NS(TI99_CCFDC, bus::ti99::peb, corcomp_fdca_device) +DECLARE_DEVICE_TYPE_NS(CCDCC_PALU2, bus::ti99::peb, ccdcc_palu2_device) +DECLARE_DEVICE_TYPE_NS(CCDCC_PALU1, bus::ti99::peb, ccdcc_palu1_device) +DECLARE_DEVICE_TYPE_NS(CCFDC_PALU12, bus::ti99::peb, ccfdc_palu12_device) +DECLARE_DEVICE_TYPE_NS(CCFDC_PALU6, bus::ti99::peb, ccfdc_palu6_device) + +#endif // MAME_BUS_TI99_PEB_CORCOMP_H diff --git a/src/devices/bus/ti99/peb/peribox.cpp b/src/devices/bus/ti99/peb/peribox.cpp index ae7ec8ab0dd..b18ff6a2dcf 100644 --- a/src/devices/bus/ti99/peb/peribox.cpp +++ b/src/devices/bus/ti99/peb/peribox.cpp @@ -182,6 +182,7 @@ CRUCLK* 51||52 DBIN #include "ti_fdc.h" #include "bwg.h" #include "hfdc.h" +#include "cc_fdc.h" #include "pcode.h" #include "myarcmem.h" #include "samsmem.h" @@ -490,6 +491,8 @@ void ti99_peribox_slot_standard(device_slot_interface &device) 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); } void peribox_device::device_add_mconfig(machine_config &config) @@ -529,6 +532,8 @@ void ti99_peribox_slot_evpc(device_slot_interface &device) 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); } void peribox_ev_device::device_add_mconfig(machine_config &config) @@ -578,6 +583,8 @@ void ti99_peribox_slot_geneve(device_slot_interface &device) 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); } void peribox_gen_device::device_add_mconfig(machine_config &config) @@ -629,6 +636,8 @@ void ti99_peribox_slot_sgcpu(device_slot_interface &device) 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); } void peribox_sg_device::device_add_mconfig(machine_config &config) diff --git a/src/devices/machine/tms9901.cpp b/src/devices/machine/tms9901.cpp index fe38f72fe22..79a29fd539c 100644 --- a/src/devices/machine/tms9901.cpp +++ b/src/devices/machine/tms9901.cpp @@ -316,10 +316,15 @@ void tms9901_device::set_int_line(int n, int state) */ READ8_MEMBER( tms9901_device::read ) { - int crubit = offset & 0x01f; + return read_bit(offset)? 0x01 : 0x00; +} + +bool tms9901_device::read_bit(int bit) +{ + int crubit = bit & 0x01f; if (crubit == 0) - return m_clock_mode? 1 : 0; + return m_clock_mode; if (crubit > 15) { @@ -329,8 +334,8 @@ READ8_MEMBER( tms9901_device::read ) else { // Positive logic; should be 0 if there is no connection. - if (m_read_port.isnull()) return 0; - return m_read_port((crubit<=P6)? crubit : P6+P0-crubit); + if (m_read_port.isnull()) return false; + return m_read_port((crubit<=P6)? crubit : P6+P0-crubit)!=0; } } @@ -338,9 +343,9 @@ READ8_MEMBER( tms9901_device::read ) if (m_clock_mode) { if (crubit == 15) // bit 15 in clock mode = /INTREQ - return (m_int_pending)? 0 : 1; + return !m_int_pending; - return BIT(m_clock_read_register, crubit-1); + return BIT(m_clock_read_register, crubit-1)!=0; } else { @@ -352,7 +357,7 @@ READ8_MEMBER( tms9901_device::read ) if (crubit>INT6 && is_output(22-crubit)) return output_value(22-crubit); else - return m_read_port.isnull()? 1 : m_read_port(crubit); + return m_read_port.isnull()? true : (m_read_port(crubit)!=0); } } @@ -369,7 +374,11 @@ READ8_MEMBER( tms9901_device::read ) */ WRITE8_MEMBER( tms9901_device::write ) { - data &= 1; // clear extra bits + write_bit(offset, data!=0); +} + +void tms9901_device::write_bit(int offset, bool data) +{ int crubit = offset & 0x001f; if (crubit >= 16) diff --git a/src/devices/machine/tms9901.h b/src/devices/machine/tms9901.h index 5e838fcad10..510cd4cd18a 100644 --- a/src/devices/machine/tms9901.h +++ b/src/devices/machine/tms9901.h @@ -68,6 +68,9 @@ public: DECLARE_READ8_MEMBER( read ); DECLARE_WRITE8_MEMBER( write ); + bool read_bit(int bit); + void write_bit(int bit, bool set); + auto p_out_cb(int n) { return m_write_p[n].bind(); } auto read_cb() { return m_read_port.bind(); } auto intreq_cb() { return m_interrupt.bind(); }