// license:BSD-3-Clause // copyright-holders:Nigel Barnes // thanks-to:Andrew Trotman /********************************************************************** Poly Proteus Computer http://www.cs.otago.ac.nz/homepages/andrew/poly/Poly.htm Specifications: Processors 6809,Z80 RAM 64 Kbytes ROM 4 Kbytes Clock Z80 4 MHz (1 wait state per machine cycle) 6809 4 MHz clock, 1 MHz cycle Disk Drives One or two 8" half height drives (Shugart SA860) Double sided single density Capacity 630 Kbytes per disk (CP/M) 590 Kbytes (Flex,Polysys) Single sided can be used for compatibility Ports Standard One RS232 One Poly network Optional Total 3 RS232 One Poly network One parallel One disk drive extension Operating systems CP/M. The standard operating system for 8080/Z80 micros. FLEX. The standard operating system for 6809 micros. POLYSYS. The operating system for the POLY network. **********************************************************************/ #include "emu.h" #include "cpu/m6809/m6809.h" #include "cpu/z80/z80.h" #include "machine/6821pia.h" #include "machine/6840ptm.h" #include "machine/6850acia.h" #include "machine/mc6854.h" #include "machine/ram.h" #include "machine/input_merger.h" #include "machine/clock.h" #include "machine/wd_fdc.h" #include "bus/rs232/rs232.h" #include "bus/centronics/ctronics.h" #include "formats/flex_dsk.h" #include "formats/poly_dsk.h" #include "softlist.h" class proteus_state : public driver_device { public: proteus_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") , m_z80(*this, "z80") , m_adlc(*this, "mc6854") , m_ptm(*this, "ptm") , m_irqs(*this, "irqs") , m_pia(*this, "pia") , m_acia(*this, "acia%u", 0) , m_fdc(*this, "fdc") , m_floppy0(*this, "fdc:0") , m_floppy1(*this, "fdc:1") , m_floppy(nullptr) { } virtual void machine_reset() override; virtual void machine_start() override; DECLARE_WRITE_LINE_MEMBER(ptm_o2_callback); DECLARE_WRITE_LINE_MEMBER(ptm_o3_callback); DECLARE_READ8_MEMBER(network_r); DECLARE_WRITE8_MEMBER(network_w); DECLARE_READ8_MEMBER(drive_register_r); DECLARE_WRITE8_MEMBER(drive_register_w); DECLARE_WRITE_LINE_MEMBER(motor_w); DECLARE_READ8_MEMBER(fdc_inv_r); DECLARE_WRITE8_MEMBER(fdc_inv_w); DECLARE_FLOPPY_FORMATS(floppy_formats); DECLARE_WRITE8_MEMBER(enable_z80_w); DECLARE_WRITE8_MEMBER(enable_6809_w); void proteus(machine_config &config); void proteus_6809_mem(address_map &map); void proteus_z80_mem(address_map &map); void proteus_z80_io(address_map &map); protected: required_device m_maincpu; required_device m_z80; required_device m_adlc; required_device m_ptm; required_device m_irqs; required_device m_pia; required_device_array m_acia; required_device m_fdc; required_device m_floppy0; required_device m_floppy1; floppy_image_device *m_floppy; }; void proteus_state::machine_start() { uint8_t bitswapped[0x1000]; uint8_t *rom; /* bios rom region */ rom = memregion("bios")->base(); /* decrypt rom data lines */ for (int i = 0x0000; i<0x1000; i++) bitswapped[i] = bitswap<8>(rom[i], 3, 4, 2, 5, 1, 6, 0, 7); /* decrypt rom address lines */ for (int i = 0x0000; i<0x1000; i++) rom[i] = bitswapped[bitswap<16>(i, 15, 14, 13, 12, 10, 8, 4, 2, 0, 1, 3, 5, 6, 7, 9, 11)]; /* The Proteus BIOS contains a serial ID for copy protection. Disks also contain the OS serial ID which must match the BIOS serial ID. This can be negated by patching the serial in the BIOS to 0x0000. */ rom[0x02] = 0x00; rom[0x03] = 0x00; } void proteus_state::machine_reset() { /* system starts with 6809 running and the Z80 remains reset */ m_z80->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); } WRITE_LINE_MEMBER(proteus_state::ptm_o2_callback) { m_ptm->set_c1(state); } WRITE_LINE_MEMBER(proteus_state::ptm_o3_callback) { m_adlc->txc_w(state); m_adlc->rxc_w(!state); } READ8_MEMBER(proteus_state::network_r) { return m_adlc->read(space, offset >> 1); } WRITE8_MEMBER(proteus_state::network_w) { m_adlc->write(space, offset >> 1, data); } WRITE8_MEMBER(proteus_state::drive_register_w) { /* drive select */ switch (data & 0x03) { case 0: m_floppy = m_floppy0->get_device(); break; case 1: m_floppy = m_floppy1->get_device(); break; default: m_floppy = nullptr; break; } m_fdc->set_floppy(m_floppy); if (m_floppy) { /* side select */ m_floppy->ss_w(BIT(data, 6)); } } READ8_MEMBER(proteus_state::drive_register_r) { /* disk change */ return (m_floppy ? m_floppy->dskchg_r() : 1) << 1; } WRITE_LINE_MEMBER(proteus_state::motor_w) { if (m_floppy) m_floppy->mon_w(!state); } READ8_MEMBER(proteus_state::fdc_inv_r) { return m_fdc->gen_r(offset) ^ 0xff; } WRITE8_MEMBER(proteus_state::fdc_inv_w) { m_fdc->gen_w(offset, data ^ 0xff); } WRITE8_MEMBER(proteus_state::enable_z80_w) { logerror("%04X enable z80\n", m_maincpu->pc()); // Enable Z80 (store an address at E060 and when the 6809 comes back it'll load PC with that address) m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); m_z80->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); } WRITE8_MEMBER(proteus_state::enable_6809_w) { logerror("%04X enable 6809\n", m_z80->pc()); // TODO: this is untested, not aware of any software that uses it m_z80->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); } void proteus_state::proteus_6809_mem(address_map &map) { map(0x0000, 0xffff).ram().share(RAM_TAG); map(0xe000, 0xe003).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // Parallel port map(0xe004, 0xe005).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Terminal map(0xe008, 0xe009).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Printer map(0xe00c, 0xe00d).rw(m_acia[2], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Modem map(0xe014, 0xe014).rw(this, FUNC(proteus_state::drive_register_r), FUNC(proteus_state::drive_register_w)); map(0xe018, 0xe01b).rw(this, FUNC(proteus_state::fdc_inv_r), FUNC(proteus_state::fdc_inv_w)); // Floppy control map(0xe020, 0xe027).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)); // Timer map(0xe030, 0xe036).rw(this, FUNC(proteus_state::network_r), FUNC(proteus_state::network_w)); // Poly network map(0xe060, 0xe060).w(this, FUNC(proteus_state::enable_z80_w)); map(0xf000, 0xffff).rom().region("bios", 0); } void proteus_state::proteus_z80_mem(address_map &map) { map(0x0000, 0xffff).ram().share(RAM_TAG); } void proteus_state::proteus_z80_io(address_map &map) { map(0x00, 0x03).mirror(0xff00).rw(m_pia, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); // Parallel port map(0x04, 0x05).mirror(0xff00).rw(m_acia[0], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Terminal map(0x08, 0x09).mirror(0xff00).rw(m_acia[1], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Printer map(0x0c, 0x0d).mirror(0xff00).rw(m_acia[2], FUNC(acia6850_device::read), FUNC(acia6850_device::write)); // Modem map(0x14, 0x14).mirror(0xff00).rw(this, FUNC(proteus_state::drive_register_r), FUNC(proteus_state::drive_register_w)); map(0x18, 0x1b).mirror(0xff00).rw(this, FUNC(proteus_state::fdc_inv_r), FUNC(proteus_state::fdc_inv_w)); // Floppy control map(0x20, 0x27).mirror(0xff00).rw(m_ptm, FUNC(ptm6840_device::read), FUNC(ptm6840_device::write)); // Timer map(0x30, 0x36).mirror(0xff00).rw(this, FUNC(proteus_state::network_r), FUNC(proteus_state::network_w)); // Poly network map(0x50, 0x50).mirror(0xff00).w(this, FUNC(proteus_state::enable_6809_w)); } //Parameter 1: Baud Rate from BIOS // 00 = 9600 // 02 = 4800 // 04 = 2400 // 06 = 1200 // 08 = 600 // 0A = 300 static INPUT_PORTS_START(proteus) PORT_START("TERM_BAUD") PORT_CONFNAME(0x07, 0x00, "Terminal Baud Rate") PORT_CONFSETTING(0x00, "9600") PORT_CONFSETTING(0x01, "4800") PORT_CONFSETTING(0x02, "2400") PORT_CONFSETTING(0x03, "1200") PORT_CONFSETTING(0x04, "600") PORT_CONFSETTING(0x05, "300") PORT_CONFSETTING(0x06, "150") PORT_START("J1") PORT_CONFNAME(0x07, 0x00, "J1 Modem Baud Rate") PORT_CONFSETTING(0x00, "9600") PORT_CONFSETTING(0x01, "4800") PORT_CONFSETTING(0x02, "2400") PORT_CONFSETTING(0x03, "1200") PORT_CONFSETTING(0x04, "600") PORT_CONFSETTING(0x05, "300") PORT_START("J2") PORT_CONFNAME(0x07, 0x00, "J2 Printer Baud Rate") PORT_CONFSETTING(0x00, "9600") PORT_CONFSETTING(0x01, "4800") PORT_CONFSETTING(0x02, "2400") PORT_CONFSETTING(0x03, "1200") PORT_CONFSETTING(0x04, "600") PORT_CONFSETTING(0x05, "300") INPUT_PORTS_END FLOPPY_FORMATS_MEMBER(proteus_state::floppy_formats) FLOPPY_FLEX_FORMAT, FLOPPY_POLY_CPM_FORMAT FLOPPY_FORMATS_END static void proteus_floppies(device_slot_interface &device) { device.option_add("8dssd", FLOPPY_8_DSSD); // Shugart SA-860 } MACHINE_CONFIG_START(proteus_state::proteus) /* basic machine hardware */ MCFG_DEVICE_ADD("maincpu", MC6809, 4_MHz_XTAL) MCFG_DEVICE_PROGRAM_MAP(proteus_6809_mem) MCFG_DEVICE_ADD("z80", Z80, 4_MHz_XTAL) MCFG_DEVICE_PROGRAM_MAP(proteus_z80_mem) MCFG_DEVICE_IO_MAP(proteus_z80_io) MCFG_INPUT_MERGER_ANY_HIGH("irqs") MCFG_INPUT_MERGER_OUTPUT_HANDLER(INPUTLINE("maincpu", M6809_IRQ_LINE)) MCFG_DEVCB_CHAIN_OUTPUT(INPUTLINE("z80", INPUT_LINE_IRQ0)) /* fdc */ MCFG_FD1771_ADD("fdc", 4_MHz_XTAL / 2) MCFG_WD_FDC_HLD_CALLBACK(WRITELINE(*this, proteus_state, motor_w)) MCFG_WD_FDC_FORCE_READY MCFG_FLOPPY_DRIVE_ADD("fdc:0", proteus_floppies, "8dssd", proteus_state::floppy_formats) MCFG_FLOPPY_DRIVE_SOUND(true) MCFG_FLOPPY_DRIVE_ADD("fdc:1", proteus_floppies, "8dssd", proteus_state::floppy_formats) MCFG_FLOPPY_DRIVE_SOUND(true) /* ram */ MCFG_RAM_ADD(RAM_TAG) MCFG_RAM_DEFAULT_SIZE("64K") /* network */ MCFG_DEVICE_ADD("mc6854", MC6854, 0) MCFG_MC6854_OUT_IRQ_CB(WRITELINE("irqs", input_merger_device, in_w<0>)) MCFG_DEVICE_ADD("ptm", PTM6840, 4_MHz_XTAL / 2) MCFG_PTM6840_EXTERNAL_CLOCKS(0, 0, 0) MCFG_PTM6840_O2_CB(WRITELINE(*this, proteus_state, ptm_o2_callback)) MCFG_PTM6840_O3_CB(WRITELINE(*this, proteus_state, ptm_o3_callback)) MCFG_PTM6840_IRQ_CB(WRITELINE("irqs", input_merger_device, in_w<1>)) /* parallel port */ MCFG_DEVICE_ADD("pia", PIA6821, 0) //MCFG_PIA_READPB_HANDLER(READ8(*this, proteus_state, pia_pb_r)) //MCFG_PIA_WRITEPA_HANDLER(WRITE8(*this, proteus_state, pia_pa_w)) //MCFG_PIA_WRITEPB_HANDLER(WRITE8(*this, proteus_state, pia_pb_w)) //MCFG_PIA_CA2_HANDLER(WRITELINE(CENTRONICS_TAG, centronics_device, write_strobe)) MCFG_PIA_IRQA_HANDLER(WRITELINE("irqs", input_merger_device, in_w<2>)) MCFG_PIA_IRQB_HANDLER(WRITELINE("irqs", input_merger_device, in_w<3>)) MCFG_CENTRONICS_ADD("parallel", centronics_devices, "printer") MCFG_CENTRONICS_ACK_HANDLER(WRITELINE("pia", pia6821_device, ca1_w)) MCFG_CENTRONICS_OUTPUT_LATCH_ADD("cent_data_out", "parallel") /* terminal port */ MCFG_DEVICE_ADD("acia0", ACIA6850, 0) MCFG_ACIA6850_TXD_HANDLER(WRITELINE("terminal", rs232_port_device, write_txd)) MCFG_ACIA6850_RTS_HANDLER(WRITELINE("terminal", rs232_port_device, write_rts)) MCFG_ACIA6850_IRQ_HANDLER(WRITELINE("irqs", input_merger_device, in_w<4>)) MCFG_DEVICE_ADD("terminal", RS232_PORT, default_rs232_devices, "terminal") // TODO: ADM-31 terminal required MCFG_RS232_RXD_HANDLER(WRITELINE("acia0", acia6850_device, write_rxd)) MCFG_RS232_CTS_HANDLER(WRITELINE("acia0", acia6850_device, write_cts)) MCFG_DEVICE_ADD("acia0_clock", CLOCK, 4_MHz_XTAL / 2 / 13) // TODO: this is set using jumpers MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE("acia0", acia6850_device, write_txc)) MCFG_DEVCB_CHAIN_OUTPUT(WRITELINE("acia0", acia6850_device, write_rxc)) /* printer port */ MCFG_DEVICE_ADD("acia1", ACIA6850, 0) MCFG_ACIA6850_TXD_HANDLER(WRITELINE("printer", rs232_port_device, write_txd)) MCFG_ACIA6850_RTS_HANDLER(WRITELINE("printer", rs232_port_device, write_rts)) MCFG_ACIA6850_IRQ_HANDLER(WRITELINE("irqs", input_merger_device, in_w<5>)) MCFG_DEVICE_ADD("printer", RS232_PORT, default_rs232_devices, nullptr) MCFG_RS232_RXD_HANDLER(WRITELINE("acia1", acia6850_device, write_rxd)) MCFG_RS232_CTS_HANDLER(WRITELINE("acia1", acia6850_device, write_cts)) MCFG_DEVICE_ADD("acia1_clock", CLOCK, 4_MHz_XTAL / 2 / 13) // TODO: this is set using jumpers J2 MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE("acia1", acia6850_device, write_txc)) MCFG_DEVCB_CHAIN_OUTPUT(WRITELINE("acia1", acia6850_device, write_rxc)) /* modem port */ MCFG_DEVICE_ADD("acia2", ACIA6850, 0) MCFG_ACIA6850_TXD_HANDLER(WRITELINE("modem", rs232_port_device, write_txd)) MCFG_ACIA6850_RTS_HANDLER(WRITELINE("modem", rs232_port_device, write_rts)) MCFG_ACIA6850_IRQ_HANDLER(WRITELINE("irqs", input_merger_device, in_w<6>)) MCFG_DEVICE_ADD("modem", RS232_PORT, default_rs232_devices, nullptr) MCFG_RS232_RXD_HANDLER(WRITELINE("acia2", acia6850_device, write_rxd)) MCFG_RS232_CTS_HANDLER(WRITELINE("acia2", acia6850_device, write_cts)) MCFG_DEVICE_ADD("acia2_clock", CLOCK, 4_MHz_XTAL / 2 / 13) // TODO: this is set using jumpers J1 MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE("acia2", acia6850_device, write_txc)) MCFG_DEVCB_CHAIN_OUTPUT(WRITELINE("acia2", acia6850_device, write_rxc)) /* software lists */ MCFG_SOFTWARE_LIST_ADD("flop_list", "poly_flop") MCFG_SOFTWARE_LIST_FILTER("flop_list", "PROTEUS") MACHINE_CONFIG_END ROM_START(proteus) ROM_REGION(0x1000, "bios", 0) ROM_DEFAULT_BIOS("82") ROM_SYSTEM_BIOS(0, "82", "030982") ROMX_LOAD("proteusv30.u28", 0x0000, 0x1000, CRC(ad02dc36) SHA1(f73aff8b3d85448f603a2fe807e3a7e02550db27), ROM_BIOS(1)) ROM_SYSTEM_BIOS(1, "86", "300986") ROMX_LOAD("300986.u28", 0x0000, 0x1000, CRC(f607d396) SHA1(0b9d9d3178b5587e60da56885b9e8a83f7d5dd26), ROM_BIOS(2)) ROM_END /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ COMP( 1982, proteus, 0, 0, proteus, proteus, proteus_state, empty_init, "Polycorp", "Poly Proteus (Standalone)", MACHINE_NOT_WORKING )