// license:BSD-3-Clause // copyright-holders:Angelo Salese /************************************************************************************************** NEC PC-9801-26(K) sound card Legacy sound card for PC-98xx family, composed by a single YM2203 NOTES: - The only known difference between -26 and -26K is that former draws its clock from C-Bus root. This makes the card to misbehave with 286+ machines. TODO: - DE-9 output writes (cfr. page 419 of PC-9801 Bible, needs software testing the functionality) - understand if dips can be read by SW; - configurable irq level needs a binding flush in C-bus handling; - Eventually emulate -26 clock style, currently hardwired as -26K; **************************************************************************************************/ #include "emu.h" #include "pc9801_26.h" #include "sound/ymopn.h" #include "speaker.h" //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(PC9801_26, pc9801_26_device, "pc9801_26", "NEC PC-9801-26/K sound card") pc9801_26_device::pc9801_26_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_pc98_cbus_slot_interface(mconfig, *this) , m_opn(*this, "opn") , m_joy(*this, "joy_p%u", 1U) , m_bios(*this, "bios") , m_irq_jp(*this, "JP6A1_JP6A3") { } pc9801_26_device::pc9801_26_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : pc9801_26_device(mconfig, PC9801_26, tag, owner, clock) { } void pc9801_26_device::device_add_mconfig(machine_config &config) { SPEAKER(config, "mono").front_center(); YM2203(config, m_opn, 15.9744_MHz_XTAL / 4); // divider not verified m_opn->irq_handler().set([this] (int state) { const int int_levels[4] = { 0, 4, 6, 5 }; m_bus->int_w(int_levels[m_int_level & 3], state); }); /* * xx-- ---- IRSTx interrupt status 0/1 * --xx ---- TRIGx Trigger 1/2 * ---- xxxx */ m_opn->port_a_read_callback().set([this] () { u8 res = (BIT(m_joy_sel, 7)) ? m_joy[BIT(m_joy_sel, 6)]->read() : 0x3f; res |= m_int_level << 6; return (u8)res; }); /* * x--- ---- OUTE Output Enable (DDR?) * -x-- ---- INSL Input Select * --21 2211 OUTxy x = port / y (2-1) = number (1-3) */ m_opn->port_b_write_callback().set([this] (u8 data) { m_joy_sel = data; // TODO: guesswork, verify if (BIT(data, 7)) return; m_joy[0]->pin_6_w(BIT(~data, 0)); m_joy[0]->pin_7_w(BIT(~data, 1)); m_joy[1]->pin_6_w(BIT(~data, 2)); m_joy[1]->pin_7_w(BIT(~data, 3)); m_joy[0]->pin_8_w(BIT(~data, 4)); m_joy[1]->pin_8_w(BIT(~data, 5)); }); // TODO: verify mixing on HW // emerald stage 1 BGM uses ch. 3 for bassline, which sounds way more prominent // than the others combined (0.25 1/4 ratio even?) m_opn->add_route(0, "mono", 0.50); m_opn->add_route(1, "mono", 0.50); m_opn->add_route(2, "mono", 0.50); m_opn->add_route(3, "mono", 1.00); MSX_GENERAL_PURPOSE_PORT(config, m_joy[0], msx_general_purpose_port_devices, "joystick"); MSX_GENERAL_PURPOSE_PORT(config, m_joy[1], msx_general_purpose_port_devices, "joystick"); } // to load a different bios for slots: // -cbus0 pc9801_26,bios=N ROM_START( pc9801_26 ) ROM_REGION( 0x4000, "bios", ROMREGION_ERASEFF ) // PC9801_26k is a minor change that applies to 286+ CPUs ROM_SYSTEM_BIOS( 0, "26k", "nec26k" ) ROMX_LOAD( "26k_wyka01_00.bin", 0x0000, 0x2000, CRC(f071bf69) SHA1(f3cdef94e9fee116cf4a9b54881e77c6cd903815), ROM_SKIP(1) | ROM_BIOS(0) ) ROMX_LOAD( "26k_wyka02_00.bin", 0x0001, 0x2000, CRC(eaa01052) SHA1(5d47edae49aad591f139d5599fe04b61aefd5ecd), ROM_SKIP(1) | ROM_BIOS(0) ) // regular BIOS, for V30 and downward CPUs ROM_SYSTEM_BIOS( 1, "26", "nec26" ) ROMX_LOAD( "sound.rom", 0x0000, 0x4000, CRC(80eabfde) SHA1(e09c54152c8093e1724842c711aed6417169db23), ROM_BIOS(1) ) // following rom is unchecked and of dubious quality // we also currently mark it based off where they originally belonged to, lacking a better info ROM_SYSTEM_BIOS( 2, "26_9821", "nec26_9821" ) ROMX_LOAD( "sound_9821.rom", 0x0000, 0x4000, BAD_DUMP CRC(a21ef796) SHA1(34137c287c39c44300b04ee97c1e6459bb826b60), ROM_BIOS(2) ) ROM_END const tiny_rom_entry *pc9801_26_device::device_rom_region() const { return ROM_NAME( pc9801_26 ); } static INPUT_PORTS_START( pc9801_26 ) // On-board jumpers PORT_START("JP6A1_JP6A3") PORT_CONFNAME( 0x03, 0x03, "PC-9801-26: Interrupt level") PORT_CONFSETTING( 0x00, "INT0 (IRQ3)" ) // 2-3, 2-3 PORT_CONFSETTING( 0x02, "INT41 (IRQ10)" ) // 2-3, 1-2 PORT_CONFSETTING( 0x03, "INT5 (IRQ12)" ) // 1-2, 1-2 PORT_CONFSETTING( 0x01, "INT6 (IRQ13)" ) // 1-2, 2-3 PORT_START("JP6A2") PORT_CONFNAME( 0x07, 0x01, "PC-9801-26: Sound ROM address") PORT_CONFSETTING( 0x00, "0xc8000" ) // 1-10 PORT_CONFSETTING( 0x01, "0xcc000" ) // 2-9 PORT_CONFSETTING( 0x02, "0xd0000" ) // 3-8 PORT_CONFSETTING( 0x03, "0xd4000" ) // 4-7 PORT_CONFSETTING( 0x04, "Disable ROM") // 5-6 PORT_START("JP6A4") PORT_CONFNAME( 0x01, 0x01, "PC-9801-26: Port Base" ) PORT_CONFSETTING( 0x00, "0x088" ) // 1-4 PORT_CONFSETTING( 0x01, "0x188" ) // 2-3 INPUT_PORTS_END ioport_constructor pc9801_26_device::device_input_ports() const { return INPUT_PORTS_NAME( pc9801_26 ); } u16 pc9801_26_device::read_io_base() { return (ioport("JP6A4")->read() & 1) << 8; } void pc9801_26_device::device_start() { m_rom_base = 0; save_item(NAME(m_joy_sel)); } void pc9801_26_device::device_reset() { // read INT line m_int_level = m_irq_jp->read() & 3; } void pc9801_26_device::remap(int space_id, offs_t start, offs_t end) { if (space_id == AS_PROGRAM) { // TODO: move base in device_reset const u8 rom_setting = ioport("JP6A2")->read() & 7; static const u32 rom_addresses[8] = { 0xc8000, 0xcc000, 0xd0000, 0xd4000, 0, 0, 0, 0 }; const u32 start_address = rom_addresses[rom_setting & 7]; if (start_address != 0) { const u32 end_address = start_address + 0x3fff; logerror("map ROM at 0x%08x-0x%08x\n", start_address, end_address); m_bus->space(AS_PROGRAM).install_rom( start_address, end_address, m_bios->base() ); } else { logerror("ROM is disconnected\n"); } } else if (space_id == AS_IO) { m_bus->install_device(0x0000, 0x3fff, *this, &pc9801_26_device::io_map); } } void pc9801_26_device::io_map(address_map &map) { const u16 io_base = read_io_base(); map(0x0088 | io_base, 0x008b | io_base).rw(m_opn, FUNC(ym2203_device::read), FUNC(ym2203_device::write)).umask16(0x00ff); }