// license:BSD-3-Clause // copyright-holders:Curt Coder /********************************************************************** Luxor 55-10828 "slow" floppy disk controller emulation *********************************************************************/ /* Luxor Conkort PCB Layout ---------- 55 10900-01 |-----------------------------------| | LD1 SW1 LS132 CON2 | | S | | 4MHz S240 N8T97N | | S | | 7404 MC1458 4024 | | S | | 74276 S | | S S | | LS32 S C140E LS273 | | | | | | MB8876 ROM | | | | | | S LS32 LS156 TC5514 | | | | TC5514 | | Z80 | | LS273 | | | | LS373 | | Z80PIO | | LS245 | | S1 | | LS125 LS124 S20 DM8131 | | | |--|-----------------------------|--| |------------CON1-------------| Notes: All IC's shown. ROM - Hitachi HN462716 2Kx8 EPROM "MPI02" Z80 - Sharp LH0080A Z80A CPU Z80PIO - SGS Z8420AB1 Z80A PIO MB8876 - Mitsubishi MB8876 Floppy Disc Controller (FD1791 compatible) TC5514 - Toshiba TC5514AP-2 1Kx4 bit Static RAM DM8131 - National Semiconductor DM8131N 6-Bit Unified Bus Comparator C140E - Ferranti ULA 2C140E floppy disk data separator and mystery device N8T97N - SA N8T97N ? CON1 - ABC bus connector CON2 - 25-pin D sub floppy connector (AMP4284) SW1 - Disk drive type (SS/DS, SD/DD) S1 - ABC bus card address bit 0 S2 - S3 - S4 - S5 - S6 - S7 - S8 - S9 - LD1 - LED */ /* TODO: - needs mid-instruction wait state support from Z80 core - inverted A4/5/6/7 chip select - copy protection device (sends sector header bytes to CPU? DDEN is serial clock? code checks for either $b6 or $f7) 06F8: ld a,$2F ; SEEK 06FA: out ($BC),a 06FC: push af 06FD: push bc 06FE: ld bc,$0724 0701: push bc 0702: ld b,$07 0704: rr a 0706: call $073F 073F: DB 7E in a,($7E) ; PIO PORT B 0741: EE 08 xor $08 ; DDEN 0743: D3 7E out ($7E),a 0745: EE 08 xor $08 0747: D3 7E out ($7E),a 0749: DB 7E in a,($7E) 074B: 1F rra 074C: 1F rra 074D: 1F rra 074E: CB 11 rl c 0750: 79 ld a,c 0751: C9 ret 0709: djnz $0703 <-- jumps to middle of instruction! 0703: rlca 0704: rr a 0706: call $073F There is an alternate format function in the controller firmware that allows to set a secret byte which is stored in the sector header. Copy protected software checks the secret byte read from the sector header and refuses to start if the disk has not been appropriately formatted. */ #include "emu.h" #include "lux10828.h" #include "formats/abc800_dsk.h" #include "formats/abc800i_dsk.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** #define Z80_TAG "5a" #define Z80PIO_TAG "3a" #define MB8876_TAG "7a" //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** DEFINE_DEVICE_TYPE(LUXOR_55_10828, luxor_55_10828_device, "lux10828", "Luxor 55 10828") //------------------------------------------------- // ROM( luxor_55_10828 ) //------------------------------------------------- ROM_START( luxor_55_10828 ) ROM_REGION( 0x800, Z80_TAG, 0 ) ROM_DEFAULT_BIOS("basf6106") // ABC 830 ROM_SYSTEM_BIOS( 0, "basf6106", "BASF 6106/08" ) ROMX_LOAD( "basf .02.7c", 0x000, 0x800, CRC(5daba200) SHA1(7881933760bed3b94f27585c0a6fc43e5d5153f5), ROM_BIOS(0) ) ROM_SYSTEM_BIOS( 1, "mpi02", "MPI 51" ) ROMX_LOAD( "mpi .02.7c", 0x000, 0x800, CRC(2aac9296) SHA1(c01a62e7933186bdf7068d2e9a5bc36590544349), ROM_BIOS(1) ) ROM_SYSTEM_BIOS( 2, "mpi02n", "MPI 51 (newer)" ) ROMX_LOAD( "new mpi .02.7c", 0x000, 0x800, CRC(ab788171) SHA1(c8e29965c04c85f2f2648496ea10c9c7ff95392f), ROM_BIOS(2) ) // ABC 832 ROM_SYSTEM_BIOS( 3, "micr1015", "Micropolis 1015 (v1.4)" ) ROMX_LOAD( "micr 1.4.7c", 0x000, 0x800, CRC(a7bc05fa) SHA1(6ac3e202b7ce802c70d89728695f1cb52ac80307), ROM_BIOS(3) ) ROM_SYSTEM_BIOS( 4, "micr1115", "Micropolis 1115 (v2.3)" ) ROMX_LOAD( "micr 2.3.7c", 0x000, 0x800, CRC(f2fc5ccc) SHA1(86d6baadf6bf1d07d0577dc1e092850b5ff6dd1b), ROM_BIOS(4) ) ROM_SYSTEM_BIOS( 5, "basf6118", "BASF 6118 (v1.2)" ) ROMX_LOAD( "basf 1.2.7c", 0x000, 0x800, CRC(9ca1a1eb) SHA1(04973ad69de8da403739caaebe0b0f6757e4a6b1), ROM_BIOS(5) ) // ABC 838 ROM_SYSTEM_BIOS( 6, "basf6104", "BASF 6104, BASF 6115 (v1.0)" ) ROMX_LOAD( "basf 8 1.0.7c", 0x000, 0x800, NO_DUMP, ROM_BIOS(6) ) ROM_END //------------------------------------------------- // rom_region - device-specific ROM region //------------------------------------------------- const tiny_rom_entry *luxor_55_10828_device::device_rom_region() const { return ROM_NAME( luxor_55_10828 ); } //------------------------------------------------- // ADDRESS_MAP( luxor_55_10828_mem ) //------------------------------------------------- void luxor_55_10828_device::luxor_55_10828_mem(address_map &map) { map.global_mask(0x1fff); map(0x0000, 0x07ff).mirror(0x0800).rom().region(Z80_TAG, 0); map(0x1000, 0x13ff).mirror(0x0c00).ram(); } //------------------------------------------------- // ADDRESS_MAP( luxor_55_10828_io ) //------------------------------------------------- void luxor_55_10828_device::luxor_55_10828_io(address_map &map) { map.global_mask(0xff); map(0x70, 0x73).mirror(0x0c).rw(m_pio, FUNC(z80pio_device::read_alt), FUNC(z80pio_device::write_alt)); map(0xb0, 0xb3).mirror(0x0c).rw(FUNC(luxor_55_10828_device::fdc_r), FUNC(luxor_55_10828_device::fdc_w)); map(0xd0, 0xd0).mirror(0x0f).w(FUNC(luxor_55_10828_device::status_w)); map(0xe0, 0xe0).mirror(0x0f).w(FUNC(luxor_55_10828_device::ctrl_w)); } //------------------------------------------------- // Z80PIO //------------------------------------------------- uint8_t luxor_55_10828_device::pio_pa_r() { return m_data; } void luxor_55_10828_device::pio_pa_w(uint8_t data) { m_data = data; } uint8_t luxor_55_10828_device::pio_pb_r() { /* bit description 0 !(_DS0 & _DS1) single/double sided (0=SS, 1=DS) 1 !(_DD0 & _DD1) single/double density (0=SD, 1=DD) 2 8B pin 10 3 4 R/_BS radial/binary drive select 5 6 FDC _HDLD head loaded 7 FDC IRQ interrupt request */ uint8_t data = 0; // single/double sided drive uint8_t sw1 = m_sw1->read() & 0x0f; bool ds0 = BIT(m_sel, 0) ? BIT(sw1, 0) : 1; bool ds1 = BIT(m_sel, 1) ? BIT(sw1, 1) : 1; data |= !(ds0 && ds1); // single/double density drive bool dd0 = BIT(m_sel, 0) ? BIT(sw1, 2) : 1; bool dd1 = BIT(m_sel, 1) ? BIT(sw1, 3) : 1; data |= !(dd0 && dd1) << 1; // TODO ULA output data |= 0x4; // radial/binary drive select data |= 0x10; // head load data |= !m_fdc->hld_r() << 6; // FDC interrupt request data |= m_fdc_irq << 7; return data; } void luxor_55_10828_device::pio_pb_w(uint8_t data) { /* bit signal description 0 1 2 3 FDC _DDEN double density enable 4 5 FDC HLT head load timing 6 7 */ // double density enable m_fdc->dden_w(BIT(data, 3)); // head load timing m_fdc->hlt_w(BIT(data, 5)); } //------------------------------------------------- // z80_daisy_config daisy_chain //------------------------------------------------- static const z80_daisy_config daisy_chain[] = { { Z80PIO_TAG }, { nullptr } }; static void abc_floppies(device_slot_interface &device) { device.option_add("525sssd", FLOPPY_525_SSSD); device.option_add("525sd", FLOPPY_525_SD); device.option_add("525ssdd", FLOPPY_525_SSDD); device.option_add("525dd", FLOPPY_525_DD); device.option_add("8dsdd", FLOPPY_8_DSDD); } void luxor_55_10828_device::floppy_formats(format_registration &fr) { fr.add_mfm_containers(); fr.add(FLOPPY_ABC800I_FORMAT); fr.add(FLOPPY_ABC800_FORMAT); } void luxor_55_10828_device::fdc_intrq_w(int state) { m_fdc_irq = state; m_pio->port_b_write(state << 7); //if (state) m_maincpu->wait_w(CLEAR_LINE); } void luxor_55_10828_device::fdc_drq_w(int state) { m_fdc_drq = state; //if (state) m_maincpu->wait_w(CLEAR_LINE); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void luxor_55_10828_device::device_add_mconfig(machine_config &config) { Z80(config, m_maincpu, 4_MHz_XTAL / 2); m_maincpu->set_addrmap(AS_PROGRAM, &luxor_55_10828_device::luxor_55_10828_mem); m_maincpu->set_addrmap(AS_IO, &luxor_55_10828_device::luxor_55_10828_io); m_maincpu->set_daisy_config(daisy_chain); Z80PIO(config, m_pio, 4_MHz_XTAL / 2); m_pio->out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0); m_pio->in_pa_callback().set(FUNC(luxor_55_10828_device::pio_pa_r)); m_pio->out_pa_callback().set(FUNC(luxor_55_10828_device::pio_pa_w)); m_pio->in_pb_callback().set(FUNC(luxor_55_10828_device::pio_pb_r)); m_pio->out_pb_callback().set(FUNC(luxor_55_10828_device::pio_pb_w)); MB8876(config, m_fdc, 4_MHz_XTAL / 4); m_fdc->intrq_wr_callback().set(FUNC(luxor_55_10828_device::fdc_intrq_w)); m_fdc->drq_wr_callback().set(FUNC(luxor_55_10828_device::fdc_drq_w)); FLOPPY_CONNECTOR(config, m_floppy[0], abc_floppies, "525ssdd", luxor_55_10828_device::floppy_formats).enable_sound(true); FLOPPY_CONNECTOR(config, m_floppy[1], abc_floppies, "525ssdd", luxor_55_10828_device::floppy_formats).enable_sound(true); } //------------------------------------------------- // INPUT_PORTS( luxor_55_10828 ) //------------------------------------------------- INPUT_PORTS_START( luxor_55_10828 ) PORT_START("SW1") PORT_DIPNAME( 0x01, 0x01, "Drive 0 Sided" ) PORT_DIPLOCATION("SW1:1") PORT_DIPSETTING( 0x01, DEF_STR( Single ) ) PORT_DIPSETTING( 0x00, "Double" ) PORT_DIPNAME( 0x02, 0x02, "Drive 1 Sided" ) PORT_DIPLOCATION("SW1:2") PORT_DIPSETTING( 0x02, DEF_STR( Single ) ) PORT_DIPSETTING( 0x00, "Double" ) PORT_DIPNAME( 0x04, 0x00, "Drive 0 Density" ) PORT_DIPLOCATION("SW1:3") PORT_DIPSETTING( 0x04, DEF_STR( Single ) ) PORT_DIPSETTING( 0x00, "Double" ) PORT_DIPNAME( 0x08, 0x00, "Drive 1 Density" ) PORT_DIPLOCATION("SW1:4") PORT_DIPSETTING( 0x08, DEF_STR( Single ) ) PORT_DIPSETTING( 0x00, "Double" ) PORT_START("S1") PORT_DIPNAME( 0x01, 0x01, "Card Address" ) PORT_DIPSETTING( 0x00, "44 (ABC 832/834/850)" ) PORT_DIPSETTING( 0x01, "45 (ABC 830)" ) PORT_START("S2_S3") PORT_DIPNAME( 0x01, 0x01, "Shift Clock" ) PORT_DIPSETTING( 0x00, "2 MHz" ) PORT_DIPSETTING( 0x01, "4 MHz" ) PORT_START("S4_S5") PORT_DIPNAME( 0x01, 0x01, "Write Precompensation" ) PORT_DIPSETTING( 0x00, "Always On" ) PORT_DIPSETTING( 0x01, "Programmable" ) INPUT_PORTS_END //------------------------------------------------- // input_ports - device-specific input ports //------------------------------------------------- ioport_constructor luxor_55_10828_device::device_input_ports() const { return INPUT_PORTS_NAME( luxor_55_10828 ); } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // luxor_55_10828_device - constructor //------------------------------------------------- luxor_55_10828_device::luxor_55_10828_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, LUXOR_55_10828, tag, owner, clock), device_abcbus_card_interface(mconfig, *this), m_maincpu(*this, Z80_TAG), m_pio(*this, Z80PIO_TAG), m_fdc(*this, MB8876_TAG), m_floppy(*this, MB8876_TAG":%u", 0U), m_sw1(*this, "SW1"), m_s1(*this, "S1"), m_cs(false), m_status(0), m_data(0), m_fdc_irq(0), m_fdc_drq(0), m_wait_enable(0), m_sel(0) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void luxor_55_10828_device::device_start() { // state saving save_item(NAME(m_cs)); save_item(NAME(m_status)); save_item(NAME(m_data)); save_item(NAME(m_fdc_irq)); save_item(NAME(m_fdc_drq)); save_item(NAME(m_wait_enable)); save_item(NAME(m_sel)); // patch out protection checks (bioses basf6106/mpi02) uint8_t *rom = memregion(Z80_TAG)->base(); rom[0x00fa] = 0xff; rom[0x0336] = 0xff; rom[0x0718] = 0xff; rom[0x072c] = 0xff; rom[0x0771] = 0xff; rom[0x0788] = 0xff; } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void luxor_55_10828_device::device_reset() { m_cs = false; ctrl_w(0); m_data = 0; } //************************************************************************** // ABC BUS INTERFACE //************************************************************************** //------------------------------------------------- // abcbus_cs - //------------------------------------------------- void luxor_55_10828_device::abcbus_cs(uint8_t data) { uint8_t address = 0x2c | BIT(m_s1->read(), 0); m_cs = (data == address); } //------------------------------------------------- // abcbus_stat - //------------------------------------------------- uint8_t luxor_55_10828_device::abcbus_stat() { uint8_t data = 0xff; if (m_cs) { data = (m_status & 0xfe) | m_pio->rdy_a(); } return data; } //------------------------------------------------- // abcbus_inp - //------------------------------------------------- uint8_t luxor_55_10828_device::abcbus_inp() { uint8_t data = 0xff; if (m_cs) { m_pio->strobe_a(0); m_pio->strobe_a(1); if (!BIT(m_status, 6)) { data = m_data; } } return data; } //------------------------------------------------- // abcbus_out - //------------------------------------------------- void luxor_55_10828_device::abcbus_out(uint8_t data) { if (m_cs) { if (BIT(m_status, 6)) { m_data = data; } m_pio->strobe_a(0); m_pio->strobe_a(1); } } //------------------------------------------------- // abcbus_c1 - //------------------------------------------------- void luxor_55_10828_device::abcbus_c1(uint8_t data) { if (m_cs) { m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); } } //------------------------------------------------- // abcbus_c3 - //------------------------------------------------- void luxor_55_10828_device::abcbus_c3(uint8_t data) { if (m_cs) { m_maincpu->reset(); } } //************************************************************************** // IMPLEMENTATION //************************************************************************** //------------------------------------------------- // ctrl_w - //------------------------------------------------- void luxor_55_10828_device::ctrl_w(uint8_t data) { /* bit signal description 0 SEL 0 1 SEL 1 2 SEL 2 3 _MOT ON 4 SIDE 5 _PRECOMP ON 6 _WAIT ENABLE 7 FDC _MR */ if (machine().side_effects_disabled()) return; // drive selection m_sel = data & 0x03; floppy_image_device *floppy = nullptr; if (BIT(m_sel, 0)) floppy = m_floppy[0]->get_device(); if (BIT(m_sel, 1)) floppy = m_floppy[1]->get_device(); m_fdc->set_floppy(floppy); if (floppy) { // motor enable floppy->mon_w(BIT(data, 3)); // side select floppy->ss_w(!BIT(data, 4)); } // wait enable m_wait_enable = BIT(data, 6); // FDC master reset m_fdc->mr_w(BIT(data, 7)); } //------------------------------------------------- // status_w - //------------------------------------------------- void luxor_55_10828_device::status_w(uint8_t data) { /* bit description 0 _INT to main Z80 1 2 3 4 5 6 LS245 DIR 7 */ if (machine().side_effects_disabled()) return; m_status = data & 0xfe; // interrupt m_slot->irq_w(BIT(data, 0) ? ASSERT_LINE : CLEAR_LINE); } //------------------------------------------------- // fdc_r - //------------------------------------------------- uint8_t luxor_55_10828_device::fdc_r(offs_t offset) { if (machine().side_effects_disabled()) return 0xff; uint8_t data = 0xff; if (m_wait_enable) { data = m_fdc->read(offset); } else if (!m_fdc_irq && !m_fdc_drq) { //m_maincpu->wait_w(ASSERT_LINE); } else { data = m_fdc->read(offset); } return data; } //------------------------------------------------- // fdc_w - //------------------------------------------------- void luxor_55_10828_device::fdc_w(offs_t offset, uint8_t data) { if (machine().side_effects_disabled()) return; m_fdc->write(offset, data); if (!m_wait_enable && !m_fdc_irq && !m_fdc_drq) { //m_maincpu->wait_w(ASSERT_LINE); } }