// license:BSD-3-Clause // copyright-holders:Curt Coder /********************************************************************** Acorn FileStore E01/E01S network hard disk emulation http://chrisacorns.computinghistory.org.uk/Network/Econet.html http://chrisacorns.computinghistory.org.uk/Network/Pics/Acorn_FileStoreE01.html http://chrisacorns.computinghistory.org.uk/8bit_Upgrades/Acorn_FileStoreE01S.html http://www.heyrick.co.uk/econet/fs/emulator.html http://www.pdfio.com/k-1019481.html# **********************************************************************/ /* The FileStore E01 is an Econet station in its own right which acts as a fileserver when connected to a network. It is a single unit which does not require a monitor or keyboard. Communication with the FileStore is done via another Econet station when the FileStore is in one of two "maintenance modes" The E01 can be seen as a slimmed-down BBC computer tailored for its function as a fileserver. It has a 6502 processor at its heart along with a 6522 VIA just like the BBC. It requires a Master series Econet module to be plugged in and connects to the network via an Econet port in the same way as any other station. The FileStore E01S was Acorns second generation Filestore replacing the FileStore E01. The FileStore is a dedicated Econet fileserver, it does not support a keyboard and monitor, instead you use an Econet attached station to logon and perform administrative tasks. Hitachi HD146818P Real Time Clock Rockwell R65C102P3 CPU 2 x TMM27256D-20 white labelled EPROMs, TMSE01 MOS on left and E01 FS on the right IC20 WD2793-APL-02 floppy disc controller 2 x NEC D41464C-12 64k x 4bit NMOS RAM ICs giving 64K memory IC21 Rockwell R65C22AP or CMD G65SC22P-2 VIA behind to the right */ /* TODO: - centronics strobe - econet clock speed select - ADLC interrupts - ECONET device - artwork - hard disk E20: Rodime RO652 (-chs 306,4,17,512) E40S: Rodime RO3057S (-chs 680,5,26,512) E60S: */ #include "emu.h" #include "e01.h" #include "bus/scsi/scsihd.h" #include "softlist_dev.h" #include "formats/afs_dsk.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** #define R65C102_TAG "r65c102" #define R6522_TAG "ic21" #define WD2793_TAG "ic20" #define MC6854_TAG "mc6854" #define HD146818_TAG "hd146818" #define CENTRONICS_TAG "centronics" #define SCSIBUS_TAG "scsi" //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** DEFINE_DEVICE_TYPE(ECONET_E01, econet_e01_device, "econet_e01", "Acorn FileStore E01") DEFINE_DEVICE_TYPE(ECONET_E01S, econet_e01s_device, "econet_e01s", "Acorn FileStore E01S") econet_e01s_device::econet_e01s_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : econet_e01_device(mconfig, ECONET_E01S, tag, owner, clock, TYPE_E01S) { } //------------------------------------------------- // ROM( e01 ) //------------------------------------------------- ROM_START( e01 ) ROM_REGION( 0x10000, R65C102_TAG, 0 ) //ROM_DEFAULT_BIOS("v131") //ROM_SYSTEM_BIOS( 0, "v131", "V 1.31" ) ROM_LOAD( "0254,205-04 e01 fs", 0x0000, 0x8000, CRC(ae666c76) SHA1(0954119eb5cd09cdbadf76d60d812aa845838d5a) ) ROM_LOAD( "0254,205-03 e01 mos", 0x8000, 0x8000, CRC(a13e8014) SHA1(6f44a1a48108c60a64a1774cb30c1a59c4a6a199) ) ROM_END //------------------------------------------------- // ROM( e01s ) //------------------------------------------------- ROM_START( e01s ) ROM_REGION( 0x10000, R65C102_TAG, 0 ) //ROM_DEFAULT_BIOS("v140") //ROM_SYSTEM_BIOS( 0, "v133", "V 1.33" ) // 0282,008-02 e01s rom ROM_LOAD( "e01sv133.rom", 0x0000, 0x10000, CRC(2a4a0032) SHA1(54ad68ceae44992293ccdd64ec88ad8520deec22) ) // which label? //ROM_SYSTEM_BIOS( 1, "v140", "V 1.40" ) ROM_LOAD( "e01sv140.rom", 0x0000, 0x10000, CRC(5068fe86) SHA1(9b8740face15b5541e2375b3054988af00757931) ) // which label? ROM_END //------------------------------------------------- // rom_region - device-specific ROM region //------------------------------------------------- const tiny_rom_entry *econet_e01_device::device_rom_region() const { switch (m_variant) { default: case TYPE_E01: return ROM_NAME( e01 ); case TYPE_E01S: return ROM_NAME( e01s ); } } //------------------------------------------------- // MC146818_INTERFACE( rtc_intf ) //------------------------------------------------- void econet_e01_device::rtc_irq_w(int state) { m_rtc_irq = state; update_interrupts(); } //------------------------------------------------- // mc6854_interface adlc_intf //------------------------------------------------- void econet_e01_device::adlc_irq_w(int state) { m_adlc_irq = state; update_interrupts(); } void econet_e01_device::econet_data_w(int state) { m_econet->data_w(this, state); } void econet_e01_device::via_irq_w(int state) { m_via_irq = state; update_interrupts(); } void econet_e01_device::clk_en_w(int state) { m_clk_en = state; } void econet_e01_device::floppy_formats_afs(format_registration &fr) { fr.add(FLOPPY_AFS_FORMAT); } void econet_e01_device::fdc_irq_w(int state) { m_fdc_irq = state; update_interrupts(); } void econet_e01_device::fdc_drq_w(int state) { m_fdc_drq = state; update_interrupts(); } void econet_e01_device::scsi_bsy_w(int state) { m_scsi_ctrl_in->write_bit1(state); if (state) { m_scsibus->write_sel(0); } } void econet_e01_device::scsi_req_w(int state) { m_scsi_ctrl_in->write_bit5(state); if (!state) { m_scsibus->write_ack(0); } m_hdc_irq = !state; update_interrupts(); } //------------------------------------------------- // ADDRESS_MAP( e01_mem ) //------------------------------------------------- void econet_e01_device::e01_mem(address_map &map) { map(0x0000, 0xffff).rw(FUNC(econet_e01_device::read), FUNC(econet_e01_device::write)); map(0xfc00, 0xfc00).mirror(0x00c3).w(m_rtc, FUNC(mc146818_device::address_w)); map(0xfc04, 0xfc04).mirror(0x00c3).rw(m_rtc, FUNC(mc146818_device::data_r), FUNC(mc146818_device::data_w)); map(0xfc08, 0xfc08).mirror(0x00c0).r(FUNC(econet_e01_device::ram_select_r)).w(FUNC(econet_e01_device::floppy_w)); map(0xfc0c, 0xfc0f).mirror(0x00c0).rw(WD2793_TAG, FUNC(wd2793_device::read), FUNC(wd2793_device::write)); map(0xfc10, 0xfc1f).mirror(0x00c0).m(R6522_TAG, FUNC(via6522_device::map)); map(0xfc20, 0xfc23).mirror(0x00c0).rw(MC6854_TAG, FUNC(mc6854_device::read), FUNC(mc6854_device::write)); map(0xfc24, 0xfc24).mirror(0x00c3).rw(FUNC(econet_e01_device::network_irq_disable_r), FUNC(econet_e01_device::network_irq_disable_w)); map(0xfc28, 0xfc28).mirror(0x00c3).rw(FUNC(econet_e01_device::network_irq_enable_r), FUNC(econet_e01_device::network_irq_enable_w)); map(0xfc2c, 0xfc2c).mirror(0x00c3).portr(m_flap); map(0xfc30, 0xfc30).mirror(0x00c0).rw(FUNC(econet_e01_device::hdc_data_r), FUNC(econet_e01_device::hdc_data_w)); map(0xfc31, 0xfc31).mirror(0x00c0).r("scsi_ctrl_in", FUNC(input_buffer_device::read)); map(0xfc32, 0xfc32).mirror(0x00c0).w(FUNC(econet_e01_device::hdc_select_w)); map(0xfc33, 0xfc33).mirror(0x00c0).w(FUNC(econet_e01_device::hdc_irq_enable_w)); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- void econet_e01_device::device_add_mconfig(machine_config &config) { // basic machine hardware R65C102(config, m_maincpu, XTAL(8'000'000)); // Rockwell R65C102P3 m_maincpu->set_addrmap(AS_PROGRAM, &econet_e01_device::e01_mem); MC146818(config, m_rtc, 32.768_kHz_XTAL); m_rtc->irq().set(FUNC(econet_e01_device::rtc_irq_w)); // devices via6522_device &via(R65C22(config, R6522_TAG, 8_MHz_XTAL / 4)); via.writepa_handler().set("cent_data_out", FUNC(output_latch_device::write)); via.irq_handler().set(FUNC(econet_e01_device::via_irq_w)); MC6854(config, m_adlc, 0); m_adlc->out_irq_cb().set(FUNC(econet_e01_device::adlc_irq_w)); m_adlc->out_txd_cb().set(FUNC(econet_e01_device::econet_data_w)); WD2793(config, m_fdc, 8_MHz_XTAL / 4); m_fdc->intrq_wr_callback().set(FUNC(econet_e01_device::fdc_irq_w)); m_fdc->drq_wr_callback().set(FUNC(econet_e01_device::fdc_drq_w)); for (int i = 0; i < 2; i++) { FLOPPY_CONNECTOR(config, m_floppy[i]); m_floppy[i]->option_add("35dd", FLOPPY_35_DD); m_floppy[i]->set_default_option("35dd"); m_floppy[i]->set_formats(floppy_formats_afs); } software_list_device &softlist(SOFTWARE_LIST(config, "flop_ls_e01")); softlist.set_original("e01_flop"); CENTRONICS(config, m_centronics, centronics_devices, "printer"); m_centronics->ack_handler().set(R6522_TAG, FUNC(via6522_device::write_ca1)); output_latch_device ¢_data_out(OUTPUT_LATCH(config, "cent_data_out")); m_centronics->set_output_latch(cent_data_out); SCSI_PORT(config, m_scsibus); m_scsibus->set_data_input_buffer(m_scsi_data_in); m_scsibus->msg_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit0)); m_scsibus->bsy_handler().set(FUNC(econet_e01_device::scsi_bsy_w)); // bit1 // bit 2 0 // bit 3 0 // bit 4 NIRQ m_scsibus->req_handler().set(FUNC(econet_e01_device::scsi_req_w)); // bit5 m_scsibus->io_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit6)); m_scsibus->cd_handler().set(m_scsi_ctrl_in, FUNC(input_buffer_device::write_bit7)); m_scsibus->set_slot_device(1, "harddisk", SCSIHD, DEVICE_INPUT_DEFAULTS_NAME(SCSI_ID_0)); OUTPUT_LATCH(config, m_scsi_data_out); m_scsibus->set_output_latch(*m_scsi_data_out); INPUT_BUFFER(config, m_scsi_data_in); INPUT_BUFFER(config, m_scsi_ctrl_in); // internal ram RAM(config, m_ram); m_ram->set_default_size("64K"); } //------------------------------------------------- // INPUT_PORTS( e01 ) //------------------------------------------------- static INPUT_PORTS_START( e01 ) PORT_START("FLAP") PORT_BIT( 0x3f, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_CONFNAME( 0x40, 0x00, "Front Flap") PORT_CONFSETTING( 0x00, "Closed" ) PORT_CONFSETTING( 0x40, "Open" ) PORT_DIPNAME( 0x80, 0x00, "SW3") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) INPUT_PORTS_END //------------------------------------------------- // input_ports - device-specific input ports //------------------------------------------------- ioport_constructor econet_e01_device::device_input_ports() const { return INPUT_PORTS_NAME( e01 ); } //************************************************************************** // INLINE HELPERS //************************************************************************** //------------------------------------------------- // update_interrupts - update interrupt state //------------------------------------------------- inline void econet_e01_device::update_interrupts() { int irq = (m_via_irq || (m_hdc_ie & m_hdc_irq) || m_rtc_irq) ? ASSERT_LINE : CLEAR_LINE; int nmi = (m_fdc_irq || m_fdc_drq || (m_adlc_ie & m_adlc_irq)) ? ASSERT_LINE : CLEAR_LINE; m_maincpu->set_input_line(M6502_IRQ_LINE, irq); m_maincpu->set_input_line(M6502_NMI_LINE, nmi); } //------------------------------------------------- // network_irq_enable - network interrupt enable //------------------------------------------------- inline void econet_e01_device::network_irq_enable(int enabled) { m_adlc_ie = enabled; update_interrupts(); } //------------------------------------------------- // hdc_irq_enable - hard disk interrupt enable //------------------------------------------------- inline void econet_e01_device::hdc_irq_enable(int enabled) { m_hdc_ie = enabled; update_interrupts(); } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // econet_e01_device - constructor //------------------------------------------------- econet_e01_device::econet_e01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : econet_e01_device(mconfig, ECONET_E01, tag, owner, clock, TYPE_E01) { } econet_e01_device::econet_e01_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int variant) : device_t(mconfig, type, tag, owner, clock) , device_econet_interface(mconfig, *this) , m_maincpu(*this, R65C102_TAG) , m_fdc(*this, WD2793_TAG) , m_adlc(*this, MC6854_TAG) , m_rtc(*this, HD146818_TAG) , m_ram(*this, RAM_TAG) , m_scsibus(*this, SCSIBUS_TAG) , m_scsi_data_out(*this, "scsi_data_out") , m_scsi_data_in(*this, "scsi_data_in") , m_scsi_ctrl_in(*this, "scsi_ctrl_in") , m_floppy(*this, WD2793_TAG":%u", 0U) , m_rom(*this, R65C102_TAG) , m_centronics(*this, CENTRONICS_TAG) , m_flap(*this, "FLAP") , m_led(*this, "led_0") , m_adlc_ie(0) , m_hdc_ie(0) , m_rtc_irq(CLEAR_LINE) , m_via_irq(CLEAR_LINE) , m_hdc_irq(CLEAR_LINE) , m_fdc_irq(CLEAR_LINE) , m_fdc_drq(CLEAR_LINE) , m_adlc_irq(CLEAR_LINE) , m_clk_en(0) , m_ram_en(false) , m_variant(variant) , m_clk_timer(nullptr) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void econet_e01_device::device_start() { m_led.resolve(); // allocate timers m_clk_timer = timer_alloc(FUNC(econet_e01_device::clk_tick), this); // register for state saving save_item(NAME(m_adlc_ie)); save_item(NAME(m_hdc_ie)); save_item(NAME(m_rtc_irq)); save_item(NAME(m_via_irq)); save_item(NAME(m_hdc_irq)); save_item(NAME(m_fdc_drq)); save_item(NAME(m_adlc_irq)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void econet_e01_device::device_reset() { m_clk_timer->adjust(attotime::zero, 0, attotime::from_hz(200000)); m_ram_en = false; } //------------------------------------------------- // clk_tick - update our clock output //------------------------------------------------- TIMER_CALLBACK_MEMBER(econet_e01_device::clk_tick) { if (m_clk_en) { m_econet->clk_w(this, 1); m_econet->clk_w(this, 0); } } //------------------------------------------------- // read - //------------------------------------------------- uint8_t econet_e01_device::read(offs_t offset) { uint8_t data; if (m_ram_en) { data = m_ram->pointer()[offset]; } else { data = m_rom->base()[offset]; } return data; } //------------------------------------------------- // write - //------------------------------------------------- void econet_e01_device::write(offs_t offset, uint8_t data) { m_ram->pointer()[offset] = data; } //------------------------------------------------- // eprom_r - ROM/RAM select read //------------------------------------------------- uint8_t econet_e01_device::ram_select_r() { if (!machine().side_effects_disabled()) m_ram_en = true; return 0; } //------------------------------------------------- // floppy_w - floppy control write //------------------------------------------------- void econet_e01_device::floppy_w(uint8_t data) { /* bit description 0 floppy 1 select 1 floppy 2 select 2 floppy side select 3 NVRAM select 4 floppy density 5 floppy master reset 6 floppy test 7 mode LED */ // floppy select floppy_image_device *floppy = nullptr; if (!BIT(data, 0)) floppy = m_floppy[0]->get_device(); if (!BIT(data, 1)) floppy = m_floppy[1]->get_device(); m_fdc->set_floppy(floppy); // floppy side select if (floppy) floppy->ss_w(BIT(data, 2)); // TODO NVRAM select //mc146818_stby_w(m_rtc, BIT(data, 3)); // floppy density m_fdc->dden_w(BIT(data, 4)); // floppy master reset m_fdc->mr_w(BIT(data, 5)); // TODO floppy test // mode LED m_led = BIT(data, 7); } //------------------------------------------------- // network_irq_disable_r - //------------------------------------------------- uint8_t econet_e01_device::network_irq_disable_r() { if (!machine().side_effects_disabled()) network_irq_enable(0); return 0; } //------------------------------------------------- // network_irq_disable_w - //------------------------------------------------- void econet_e01_device::network_irq_disable_w(uint8_t data) { network_irq_enable(0); } //------------------------------------------------- // network_irq_enable_r - //------------------------------------------------- uint8_t econet_e01_device::network_irq_enable_r() { if (!machine().side_effects_disabled()) network_irq_enable(1); return 0; } //------------------------------------------------- // network_irq_enable_w - //------------------------------------------------- void econet_e01_device::network_irq_enable_w(uint8_t data) { network_irq_enable(1); } //------------------------------------------------- // hdc_data_r - //------------------------------------------------- uint8_t econet_e01_device::hdc_data_r() { uint8_t data = m_scsi_data_in->read(); if (!machine().side_effects_disabled()) m_scsibus->write_ack(1); return data; } //------------------------------------------------- // hdc_data_w - //------------------------------------------------- void econet_e01_device::hdc_data_w(uint8_t data) { m_scsi_data_out->write(data); m_scsibus->write_ack(1); } //------------------------------------------------- // hdc_select_w - //------------------------------------------------- void econet_e01_device::hdc_select_w(uint8_t data) { m_scsibus->write_sel(1); } //------------------------------------------------- // hdc_irq_enable_w - //------------------------------------------------- void econet_e01_device::hdc_irq_enable_w(uint8_t data) { hdc_irq_enable(BIT(data, 0)); } //------------------------------------------------- // econet_clk_w - //------------------------------------------------- void econet_e01_device::econet_data(int state) { m_adlc->set_rx(state); } //------------------------------------------------- // econet_clk_w - //------------------------------------------------- void econet_e01_device::econet_clk(int state) { m_adlc->rxc_w(state); m_adlc->txc_w(state); }