diff options
Diffstat (limited to 'src/devices/bus/isa/ibm_mfc.cpp')
-rw-r--r-- | src/devices/bus/isa/ibm_mfc.cpp | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/src/devices/bus/isa/ibm_mfc.cpp b/src/devices/bus/isa/ibm_mfc.cpp new file mode 100644 index 00000000000..293162aa70a --- /dev/null +++ b/src/devices/bus/isa/ibm_mfc.cpp @@ -0,0 +1,484 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett +/*************************************************************************** + + ISA 8 bit IBM PC Music Feature Card + + TODO: + - YM-2164 + - MIDI + - IRQ/base address selection + + Notes: + - Some software does not function correctly at higher CPU speeds + (e.g. the Sierra games and Yamaha Compose/PlayRec) + +***************************************************************************/ + + +#include "emu.h" +#include "ibm_mfc.h" +#include "cpu/z80/z80.h" +#include "machine/clock.h" +#include "machine/pit8253.h" + + +//------------------------------------------------- +// Constants +//------------------------------------------------- + +#define TCR_TAC 0x01 +#define TCR_TBC 0x02 +#define TCR_TAE 0x04 +#define TCR_TBE 0x08 +#define TCR_EXT8 0x10 +#define TCR_TMSK 0x40 +#define TCR_IBE 0x80 + +#define TSR_TAS 0x01 +#define TSR_TBS 0x02 +#define TSR_TCS 0x80 + +enum +{ + PC_IRQ_TIMERA, + PC_IRQ_TIMERB, + PC_IRQ_RXRDY, + PC_IRQ_TXRDY +}; + +enum +{ + Z80_IRQ_YM, + Z80_IRQ_RXRDY, + Z80_IRQ_TXRDY, + Z80_IRQ_MIDI_RXRDY, + Z80_IRQ_MIDI_TXRDY +}; + + +//------------------------------------------------- +// Globals +//------------------------------------------------- + +const device_type ISA8_IBM_MFC = &device_creator<isa8_ibm_mfc_device>; + + +//------------------------------------------------- +// Interrupt handling +//------------------------------------------------- + +void isa8_ibm_mfc_device::set_pc_interrupt(int src, int state) +{ + if (state) + m_pc_irq_state |= 1 << src; + else + m_pc_irq_state &= ~(1 << src); + + update_pc_interrupts(); +} + +void isa8_ibm_mfc_device::update_pc_interrupts(void) +{ + // IRQs enabled? + if (m_tcr & TCR_IBE) + { + // IRQs unmasked? + if (m_tcr & TCR_TMSK) + { + m_isa->irq3_w(m_pc_irq_state ? ASSERT_LINE : CLEAR_LINE); + } + } +} + +void isa8_ibm_mfc_device::set_z80_interrupt(int src, int state) +{ + if (state) + m_z80_irq_state |= 1 << src; + else + m_z80_irq_state &= ~(1 << src); + + update_z80_interrupts(); +} + +void isa8_ibm_mfc_device::update_z80_interrupts(void) +{ + m_cpu->set_input_line(0, m_z80_irq_state ? ASSERT_LINE : CLEAR_LINE); +} + + +//------------------------------------------------- +// Z80 memory map +//------------------------------------------------- + +static ADDRESS_MAP_START( prg_map, AS_PROGRAM, 8, isa8_ibm_mfc_device ) + AM_RANGE(0x0000, 0x7fff) AM_ROM + AM_RANGE(0x8000, 0x8000) AM_RAM // Unknown - tested on startup + AM_RANGE(0xbfff, 0xbfff) AM_RAM // Unknown - tested on startup + AM_RANGE(0xc000, 0xdfff) AM_RAM + AM_RANGE(0xe000, 0xffff) AM_RAM +ADDRESS_MAP_END + +static ADDRESS_MAP_START( io_map, AS_IO, 8, isa8_ibm_mfc_device ) + ADDRESS_MAP_UNMAP_HIGH + ADDRESS_MAP_GLOBAL_MASK(0xff) + AM_RANGE(0x00, 0x01) AM_DEVREADWRITE("ym2151", ym2151_device, read, write) + AM_RANGE(0x10, 0x10) AM_DEVREADWRITE("d71051", i8251_device, data_r, data_w) + AM_RANGE(0x11, 0x11) AM_DEVREADWRITE("d71051", i8251_device, status_r, control_w) + AM_RANGE(0x20, 0x23) AM_DEVREADWRITE("d71055c_1", i8255_device, read, write) +ADDRESS_MAP_END + + +//------------------------------------------------- +// Jumpers and DIP switches +//------------------------------------------------- + +static INPUT_PORTS_START( ibm_mfc ) + PORT_START("J1") + PORT_DIPNAME( 0x07, 0x03, "IBM MFC J1: IRQ" ) + PORT_DIPSETTING( 0x02, "2" ) + PORT_DIPSETTING( 0x03, "3" ) + PORT_DIPSETTING( 0x04, "4" ) + PORT_DIPSETTING( 0x05, "5" ) + PORT_DIPSETTING( 0x06, "6" ) + PORT_DIPSETTING( 0x07, "7" ) + + PORT_START("SW1") + PORT_DIPNAME( 0x03, 0x00, "IBM MFC SW1: Base Address" ) + PORT_DIPSETTING( 0x00, "2A00" ) + PORT_DIPSETTING( 0x01, "2A10" ) + PORT_DIPSETTING( 0x02, "2A20" ) + PORT_DIPSETTING( 0x03, "2A30" ) +INPUT_PORTS_END + +//------------------------------------------------- +// D71055C PPI (PC) +//------------------------------------------------- + +READ8_MEMBER( isa8_ibm_mfc_device::ppi0_i_a ) +{ + // Read data from the Z80 PIU + return m_d71055c_1->pa_r(); +} + +WRITE8_MEMBER( isa8_ibm_mfc_device::ppi0_o_b ) +{ + // Write data to the Z80 PIU - no action required +} + +WRITE8_MEMBER( isa8_ibm_mfc_device::ppi0_o_c ) +{ + // PC Port B /OBF (C1) -> Z80 Port B /STB (C2) + m_d71055c_1->pc2_w(BIT(data, 1)); + + // PC Port A IBF (C5) -> Z80 Port A /ACK (C6) +#if 0 // TODO + m_d71055c_1->pc6_w(!BIT(data, 5)); +#else + if (!BIT(data, 5) && BIT(m_pc_ppi_c, 5)) + m_d71055c_1->pc6_w(0); +#endif + + // Bit 0 (INTRB) is TxRDY + set_pc_interrupt(PC_IRQ_TXRDY, BIT(data, 0)); + + // Bit 3 (INTRA) is RxRDY + set_pc_interrupt(PC_IRQ_RXRDY, BIT(data, 3)); + + m_pc_ppi_c = data; +} + +READ8_MEMBER( isa8_ibm_mfc_device::ppi0_i_c ) +{ + // Receive data bit 8 + return BIT(m_z80_ppi_c, 5) << 7; +} + +//------------------------------------------------- +// D71055C PPI (Z80) +//------------------------------------------------- + +WRITE8_MEMBER( isa8_ibm_mfc_device::ppi1_o_a ) +{ + // Write data to the PC PIU - no action required +} + +READ8_MEMBER( isa8_ibm_mfc_device::ppi1_i_b ) +{ + // Read data from the PC PIU + return m_d71055c_0->pb_r(); +} + +WRITE8_MEMBER( isa8_ibm_mfc_device::ppi1_o_c ) +{ + // PortA /OBF (C7) -> PortA /STB (C2) + m_d71055c_0->pc4_w(BIT(data, 7)); + + // PortB IBF (C1) -> PortB /ACK (C2) +#if 0 // TODO + m_d71055c_0->pc2_w(!BIT(data, 1)); +#else + if (!BIT(data, 1) && BIT(m_z80_ppi_c, 1)) + m_d71055c_0->pc2_w(0); +#endif + + set_z80_interrupt(Z80_IRQ_TXRDY, BIT(data, 3)); + set_z80_interrupt(Z80_IRQ_RXRDY, BIT(data, 0)); + + m_z80_ppi_c = data; +} + +//------------------------------------------------- +// D8253 PIT +//------------------------------------------------- + +WRITE_LINE_MEMBER( isa8_ibm_mfc_device::d8253_out0 ) +{ + if (m_tcr & TCR_TAE) + set_pc_interrupt(PC_IRQ_TIMERA, 1); +} + +WRITE_LINE_MEMBER( isa8_ibm_mfc_device::d8253_out1 ) +{ + if (m_tcr & TCR_TBE) + set_pc_interrupt(PC_IRQ_TIMERB, 1); +} + + +//------------------------------------------------- +// uPD71051 USART +//------------------------------------------------- + +WRITE_LINE_MEMBER( isa8_ibm_mfc_device::write_usart_clock ) +{ + m_d71051->write_txc(state); + m_d71051->write_rxc(state); +} + +//------------------------------------------------- +// YM-2164 +//------------------------------------------------- + + +WRITE_LINE_MEMBER(isa8_ibm_mfc_device::ibm_mfc_ym_irq) +{ + set_z80_interrupt(Z80_IRQ_YM, state); +} + + +//------------------------------------------------- +// Machine config +//------------------------------------------------- + +static MACHINE_CONFIG_FRAGMENT( ibm_mfc ) + MCFG_CPU_ADD("ibm_mfc", Z80, XTAL_11_8MHz / 2) + MCFG_CPU_PROGRAM_MAP(prg_map) + MCFG_CPU_IO_MAP(io_map) + + MCFG_DEVICE_ADD("d71055c_0", I8255, 0) + MCFG_I8255_IN_PORTA_CB(READ8(isa8_ibm_mfc_device, ppi0_i_a)) + MCFG_I8255_OUT_PORTB_CB(WRITE8(isa8_ibm_mfc_device, ppi0_o_b)) + MCFG_I8255_IN_PORTC_CB(READ8(isa8_ibm_mfc_device, ppi0_i_c)) + MCFG_I8255_OUT_PORTC_CB(WRITE8(isa8_ibm_mfc_device, ppi0_o_c)) + + MCFG_DEVICE_ADD("d71055c_1", I8255, 0) + MCFG_I8255_OUT_PORTA_CB(WRITE8(isa8_ibm_mfc_device, ppi1_o_a)) + MCFG_I8255_IN_PORTB_CB(READ8(isa8_ibm_mfc_device, ppi1_i_b)) + MCFG_I8255_OUT_PORTC_CB(WRITE8(isa8_ibm_mfc_device, ppi1_o_c)) + + MCFG_DEVICE_ADD("d71051", I8251, 0) + + MCFG_DEVICE_ADD("usart_clock", CLOCK, XTAL_4MHz / 8) // 500KHz + MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE(isa8_ibm_mfc_device, write_usart_clock)) + + MCFG_DEVICE_ADD("d8253", PIT8253, 0) + MCFG_PIT8253_CLK0(XTAL_4MHz / 8) + MCFG_PIT8253_OUT0_HANDLER(WRITELINE(isa8_ibm_mfc_device, d8253_out0)) + MCFG_PIT8253_CLK1(0) + MCFG_PIT8253_OUT1_HANDLER(WRITELINE(isa8_ibm_mfc_device, d8253_out1)) + MCFG_PIT8253_CLK2(XTAL_4MHz / 2) + MCFG_PIT8253_OUT2_HANDLER(DEVWRITELINE("d8253", pit8253_device, write_clk1)) + + MCFG_SPEAKER_STANDARD_STEREO("ymleft", "ymright") + MCFG_YM2151_ADD("ym2151", XTAL_4MHz) + MCFG_YM2151_IRQ_HANDLER(WRITELINE(isa8_ibm_mfc_device, ibm_mfc_ym_irq)) + MCFG_SOUND_ROUTE(0, "ymleft", 1.00) + MCFG_SOUND_ROUTE(1, "ymright", 1.00) +MACHINE_CONFIG_END + + +//------------------------------------------------- +// ISA interface +//------------------------------------------------- + +READ8_MEMBER( isa8_ibm_mfc_device::ibm_mfc_r ) +{ + UINT8 val = 0xff; + + switch (offset) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + { + val = m_d71055c_0->read(space, offset); + break; + } + + case 0xc: + case 0xd: + case 0xe: + case 0xf: + { + val = (m_pc_irq_state ? 0x80 : 0) | (m_pc_irq_state & 3); + break; + } + + default: + { + fatalerror("Unhandled IBM MFC read from %d\n", offset); + } + } + + return val; +} + +WRITE8_MEMBER( isa8_ibm_mfc_device::ibm_mfc_w ) +{ + switch (offset) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + { + machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(1000)); + m_d71055c_0->write(space, offset, data); + break; + } + + case 0x4: + case 0x5: + case 0x6: + case 0x7: + { + m_d8253->write(space, offset & 3, data); + break; + } + + case 0x8: + case 0x9: + case 0xa: + case 0xb: + { + m_tcr = data; + + if (~m_tcr & TCR_TAC) + set_pc_interrupt(PC_IRQ_TIMERA, 0); + + if (~m_tcr & TCR_TBC) + set_pc_interrupt(PC_IRQ_TIMERB, 0); + + m_d71051->write_dsr((m_tcr & TCR_EXT8) ? 1 : 0); + + break; + } + + case 0xc: + case 0xd: + case 0xe: + case 0xf: + { + // TSR is read-only but Yamaha software attempts to write to it + break; + } + } +} + + +//------------------------------------------------- +// ROM definition +//------------------------------------------------- + +ROM_START( ibm_mfc ) + ROM_REGION( 0x8000, "ibm_mfc", 0 ) + ROM_LOAD( "xc215 c 0.bin", 0x0000, 0x8000, CRC(28c58a4f) SHA1(e7edf28d20e6c146e3144526c89cd6beea64663b) ) +ROM_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor isa8_ibm_mfc_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( ibm_mfc ); +} + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor isa8_ibm_mfc_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( ibm_mfc ); +} + + +//------------------------------------------------- +// rom_region - return a pointer to the device's +// internal ROM region +//------------------------------------------------- + +const rom_entry *isa8_ibm_mfc_device::device_rom_region() const +{ + return ROM_NAME( ibm_mfc ); +} + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// isa8_ibm_mfc_device - constructor +//------------------------------------------------- + +isa8_ibm_mfc_device::isa8_ibm_mfc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, ISA8_IBM_MFC, "IBM PC Music Feature Card", tag, owner, clock, "ibm_mfc", __FILE__), + device_isa8_card_interface(mconfig, *this), + m_cpu(*this, "ibm_mfc"), + m_ym2151(*this, "ym2151"), + m_d8253(*this, "d8253"), + m_d71051(*this, "d71051"), + m_d71055c_0(*this, "d71055c_0"), + m_d71055c_1(*this, "d71055c_1") +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void isa8_ibm_mfc_device::device_start() +{ + set_isa_device(); + m_isa->install_device(0x2a20, 0x2a20 + 15, 0, 0, read8_delegate(FUNC(isa8_ibm_mfc_device::ibm_mfc_r), this), write8_delegate(FUNC(isa8_ibm_mfc_device::ibm_mfc_w), this)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void isa8_ibm_mfc_device::device_reset() +{ + m_tcr = 0; + m_d71051->write_dsr(0); + m_pc_irq_state = 0; + m_z80_irq_state = 0; +} |