diff --git a/src/mame/ne/z80ne.cpp b/src/mame/ne/z80ne.cpp index b8aaba82e47..0dfe79334aa 100644 --- a/src/mame/ne/z80ne.cpp +++ b/src/mame/ne/z80ne.cpp @@ -1,6 +1,7 @@ // license:BSD-3-Clause // copyright-holders:Roberto Lavarone /****************************************************************************** + Nuova Elettronica Z80NE system driver Preliminary driver @@ -8,7 +9,7 @@ Roberto Lavarone, 2009-01-05 Thanks go to: - Roberto Bazzano: www.z80ne.com + Roberto Bazzano: http://www.z80ne.com/ Z80NE memory map @@ -31,7 +32,6 @@ LX.392 32K Memory expansion board range short description - LX.389 Printer Interface range short description 02-03 @@ -123,21 +123,29 @@ Natural Keyboard and Paste: N1000=11=22=33=44=55=66=77=88=99=N1000= Press Ctrl+0 to review the data. - *********************************************************************************************************/ /* Core includes */ #include "emu.h" -#include "z80ne.h" #include "cpu/z80/z80.h" +#include "imagedev/cassette.h" +#include "imagedev/floppy.h" +#include "machine/ay31015.h" +#include "machine/clock.h" +#include "machine/kr2376.h" #include "machine/ram.h" +#include "machine/wd_fdc.h" +#include "video/mc6847.h" #include "softlist_dev.h" #include "speaker.h" #include "formats/dmk_dsk.h" +//#define VERBOSE 1 +#include "logmacro.h" + /* Layout */ #include "z80ne.lh" #include "z80net.lh" @@ -146,9 +154,917 @@ Natural Keyboard and Paste: /****************************************************************************** - Memory Maps + Constants ******************************************************************************/ +namespace { + +#define Z80NE_CPU_SPEED_HZ 1920000 /* 1.92 MHz */ + +#define LX383_KEYS 16 +#define LX383_DOWNSAMPLING 16 + +#define LX385_TAPE_SAMPLE_FREQ 38400 + +/* wave duration threshold */ +enum z80netape_speed +{ + TAPE_300BPS = 300, /* 300 bps */ + TAPE_600BPS = 600, /* 600 bps */ + TAPE_1200BPS = 1200 /* 1200 bps */ +}; + +struct z80ne_cass_data_t { + struct { + int length = 0; /* time cassette level is at input.level */ + int level = 0; /* cassette level */ + int bit = 0; /* bit being read */ + } input; + struct { + int length = 0; /* time cassette level is at output.level */ + int level = 0; /* cassette level */ + int bit = 0; /* bit to output */ + } output; + z80netape_speed speed; /* 300 - 600 - 1200 */ + int wave_filter = 0; + int wave_length = 0; + int wave_short = 0; + int wave_long = 0; +}; + + +class z80ne_state : public driver_device +{ +public: + z80ne_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_uart(*this, "uart"), + m_uart_clock(*this, "uart_clock"), + m_maincpu(*this, "maincpu"), + m_cassette1(*this, "cassette"), + m_cassette2(*this, "cassette2"), + m_ram(*this, RAM_TAG), + m_bank1(*this, "bank1"), + m_bank2(*this, "bank2"), + m_bank3(*this, "bank3"), + m_bank4(*this, "bank4"), + m_io_row0(*this, "ROW0"), + m_io_row1(*this, "ROW1"), + m_io_ctrl(*this, "CTRL"), + m_io_rst(*this, "RST"), + m_io_lx_385(*this, "LX.385"), + m_lx383_digits(*this, "digit%u", 0U), + m_rom(*this, "maincpu"), + m_mram(*this, "mainram") + { } + + void z80ne(machine_config &config); + void init_z80ne(); + + DECLARE_INPUT_CHANGED_MEMBER(z80ne_reset); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + + void base_reset(); + void save_state_vars(); + + static void floppy_formats(format_registration &fr); + + uint8_t m_lx383_scan_counter = 0; + uint8_t m_lx383_key[LX383_KEYS]{}; + int m_lx383_downsampler = 0; + uint8_t m_lx385_ctrl = 0; + emu_timer *m_cassette_timer = nullptr; + emu_timer *m_kbd_timer = nullptr; + z80ne_cass_data_t m_cass_data; + + uint8_t lx383_r(); + void lx383_w(offs_t offset, uint8_t data); + uint8_t lx385_ctrl_r(); + void lx385_ctrl_w(uint8_t data); + DECLARE_WRITE_LINE_MEMBER(lx385_uart_tx_clock_w); + + TIMER_CALLBACK_MEMBER(cassette_tc); + TIMER_CALLBACK_MEMBER(kbd_scan); + TIMER_CALLBACK_MEMBER(pulse_nmi); + + memory_passthrough_handler m_rom_shadow_tap; + required_device m_uart; + required_device m_uart_clock; + required_device m_maincpu; + required_device m_cassette1; + required_device m_cassette2; + optional_device m_ram; + optional_memory_bank m_bank1; + optional_memory_bank m_bank2; + optional_memory_bank m_bank3; + optional_memory_bank m_bank4; + required_ioport m_io_row0; + required_ioport m_io_row1; + required_ioport m_io_ctrl; + required_ioport m_io_rst; + required_ioport m_io_lx_385; + output_finder<8> m_lx383_digits; + required_region_ptr m_rom; + optional_shared_ptr m_mram; + + emu_timer *m_timer_nmi = nullptr; + + cassette_image_device *cassette_device_image(); + +private: + void mem_map(address_map &map); + void io_map(address_map &map); +}; + +class z80net_state : public z80ne_state +{ +public: + z80net_state(const machine_config &mconfig, device_type type, const char *tag) : + z80ne_state(mconfig, type, tag), + m_videoram(*this, "videoram"), + m_vdg(*this, "mc6847"), + m_lx387_kr2376(*this, "lx387_kr2376"), + m_io_lx387_brk(*this, "LX387_BRK"), + m_io_modifiers(*this, "MODIFIERS") + { + } + + void lx387(machine_config &config); + void z80net(machine_config &config); + + DECLARE_INPUT_CHANGED_MEMBER(z80net_nmi); + +protected: + virtual void machine_reset() override; + + DECLARE_READ_LINE_MEMBER(lx387_shift_r); + DECLARE_READ_LINE_MEMBER(lx387_control_r); + uint8_t lx387_data_r(); + uint8_t lx388_mc6847_videoram_r(offs_t offset); + uint8_t lx388_read_field_sync(); + + required_shared_ptr m_videoram; + required_device m_vdg; + required_device m_lx387_kr2376; + required_ioport m_io_lx387_brk; + required_ioport m_io_modifiers; + + void reset_lx387(); + + void io_map(address_map &map); + +private: + void mem_map(address_map &map); +}; + +class z80netb_state : public z80net_state +{ +public: + z80netb_state(const machine_config &mconfig, device_type type, const char *tag) : + z80net_state(mconfig, type, tag) + { + } + + void z80netb(machine_config &config); + +protected: + virtual void machine_reset() override; + +private: + void mem_map(address_map &map); +}; + +class z80netf_state : public z80netb_state +{ +public: + z80netf_state(const machine_config &mconfig, device_type type, const char *tag) : + z80netb_state(mconfig, type, tag), + m_io_config(*this, "CONFIG"), + m_floppy(*this, "wd1771:%u", 0U), + m_wd1771(*this, "wd1771"), + m_drv_led(*this, "drv%u", 0U) + { + } + + void z80netf(machine_config &config); + +private: + virtual void machine_start() override; + virtual void machine_reset() override; + virtual void driver_start() override; + + struct wd17xx_state_t + { + int drq = 0; + int intrq = 0; + uint8_t drive = 0; /* current drive */ + uint8_t head = 0; /* current head */ + }; + + void mem_map(address_map &map); + void io_map(address_map &map); + + void lx390_motor_w(uint8_t data); + uint8_t lx390_fdc_r(offs_t offset); + void lx390_fdc_w(offs_t offset, uint8_t data); + + void reset_lx390_banking(); + + required_ioport m_io_config; + required_device_array m_floppy; + required_device m_wd1771; + wd17xx_state_t m_wd17xx_state; + output_finder<2> m_drv_led; +}; + + + +/****************************************************************************** + I/O +******************************************************************************/ + +/* timer to read cassette waveforms */ + +cassette_image_device* z80ne_state::cassette_device_image() +{ + if (m_lx385_ctrl & 0x08) + return m_cassette2; + else + return m_cassette1; +} + +TIMER_CALLBACK_MEMBER(z80ne_state::cassette_tc) +{ + uint8_t cass_ws = 0; + m_cass_data.input.length++; + + cass_ws = ((cassette_device_image())->input() > +0.02) ? 1 : 0; + + if ((cass_ws ^ m_cass_data.input.level) & cass_ws) + { + m_cass_data.input.level = cass_ws; + m_cass_data.input.bit = ((m_cass_data.input.length < m_cass_data.wave_filter) || (m_cass_data.input.length > 0x20)) ? 1 : 0; + m_cass_data.input.length = 0; + m_uart->write_si(m_cass_data.input.bit); + } + m_cass_data.input.level = cass_ws; + + /* saving a tape - convert the serial stream from the uart */ + m_cass_data.output.length--; + + if (!(m_cass_data.output.length)) + { + if (m_cass_data.output.level) + m_cass_data.output.level = 0; + else + { + m_cass_data.output.level=1; + cass_ws = m_uart->so_r(); + m_cass_data.wave_length = cass_ws ? m_cass_data.wave_short : m_cass_data.wave_long; + } + cassette_device_image()->output(m_cass_data.output.level ? -1.0 : +1.0); + m_cass_data.output.length = m_cass_data.wave_length; + } +} + +void z80ne_state::save_state_vars() +{ + save_item(NAME(m_lx383_scan_counter)); + save_item(NAME(m_lx383_key)); + save_item(NAME(m_lx383_downsampler)); + save_item(NAME(m_lx385_ctrl)); +} + +void z80ne_state::init_z80ne() +{ + save_state_vars(); +} + +void z80netf_state::driver_start() +{ + save_state_vars(); + + /* first two entries point to rom on reset */ + u8 *r = m_ram->pointer(); + m_bank1->configure_entry(0, r); /* RAM at 0x0000-0x03FF */ + m_bank1->configure_entries(1, 3, m_rom+0x4400, 0x0400); /* ep390, ep1390, ep2390 at 0x0000-0x03FF */ + m_bank1->configure_entry(4, m_rom+0x4000); /* ep382 at 0x0000-0x03FF */ + m_bank1->configure_entry(5, m_rom); /* ep548 at 0x0000-0x03FF */ + + m_bank2->configure_entry(0, r+0x0400); /* RAM at 0x0400 */ + m_bank2->configure_entry(1, m_rom+0x0400); /* ep548 at 0x0400-0x3FFF */ + + m_bank3->configure_entry(0, r+0x4000); /* RAM at 0x8000 */ + m_bank3->configure_entry(1, m_rom+0x4000); /* ep382 at 0x8000 */ + + m_bank4->configure_entry(0, r+0x5000); /* RAM at 0xF000 */ + m_bank4->configure_entries(1, 3, m_rom+0x4400, 0x0400); /* ep390, ep1390, ep2390 at 0xF000 */ + +} + +TIMER_CALLBACK_MEMBER(z80ne_state::kbd_scan) +{ + /* + * NE555 is connected to a 74LS93 binary counter + * 74LS93 output: + * QA-QC: column index for LEDs and keyboard + * QD: keyboard row select + * + * Port F0 input bit assignment: + * 0 QA bits 0..3 of row counter + * 1 QB + * 2 QC + * 3 QD + * 4 Control button pressed, active high + * 5 Always low + * 6 Always low + * 7 Selected button pressed, active low + * + * + */ + + uint16_t key_bits; + uint8_t ctrl; //, rst; + uint8_t i; + + /* 4-bit counter */ + --m_lx383_scan_counter; + m_lx383_scan_counter &= 0x0f; + + if ( --m_lx383_downsampler == 0 ) + { + m_lx383_downsampler = LX383_DOWNSAMPLING; + key_bits = (m_io_row1->read() << 8) | m_io_row0->read(); +// rst = m_io_rst->read(); + ctrl = m_io_ctrl->read(); + + for ( i = 0; i>= 1; + } + } +} + +TIMER_CALLBACK_MEMBER(z80ne_state::pulse_nmi) +{ + m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); +} + +void z80net_state::reset_lx387() +{ + m_lx387_kr2376->set_input_pin( kr2376_device::KR2376_DSII, 0); + m_lx387_kr2376->set_input_pin( kr2376_device::KR2376_PII, 0); +} + +void z80netf_state::reset_lx390_banking() +{ + switch (m_io_config->read() & 0x07) + { + case 0x01: /* EP382 Hex Monitor */ + if (VERBOSE) + logerror("reset_lx390_banking: banking ep382\n"); + m_bank1->set_entry(4); /* ep382 at 0x0000 for 3 cycles, then RAM */ + m_bank2->set_entry(0); /* RAM at 0x0400 */ + m_bank3->set_entry(1); /* ep382 at 0x8000 */ + m_bank4->set_entry(0); /* RAM at 0xF000 */ + break; + case 0x02: /* EP548 16k BASIC */ + if (VERBOSE) + logerror("reset_lx390_banking: banking ep548\n"); + m_bank1->set_entry(5); /* ep548 at 0x0000-0x03FF */ + m_bank2->set_entry(1); /* ep548 at 0x0400-0x3FFF */ + m_bank3->set_entry(0); /* RAM at 0x8000 */ + m_bank4->set_entry(0); /* RAM at 0xF000 */ + break; + case 0x03: /* EP390 Boot Loader for 5.5k floppy BASIC */ + if (VERBOSE) + logerror("reset_lx390_banking: banking ep390\n"); + m_bank1->set_entry(1); /* ep390 at 0x0000-0 x03FF for 3 cycles, then RAM */ + m_bank2->set_entry(0); /* RAM at 0x0400-0x3FFF */ + m_bank3->set_entry(0); /* RAM at 0x8000 */ + m_bank4->set_entry(1); /* ep390 at 0xF000 */ + break; + case 0x04: /* EP1390 Boot Loader for NE DOS 1.0/1.5 */ + if (VERBOSE) + logerror("reset_lx390_banking: banking ep1390\n"); + m_bank1->set_entry(2); /* ep1390 at 0x0000-0x03FF for 3 cycles, then RAM */ + m_bank2->set_entry(0); /* RAM at 0x0400-0x3FFF */ + m_bank3->set_entry(0); /* RAM at 0x8000 */ + m_bank4->set_entry(2); /* ep1390 at 0xF000 */ + break; + case 0x05: /* EP2390 Boot Loader for NE DOS G.1 */ + if (VERBOSE) + logerror("reset_lx390_banking: banking ep2390\n"); + m_bank1->set_entry(3); /* ep2390 at 0x0000-0x03FF for 3 cycles, then RAM */ + m_bank2->set_entry(0); /* RAM at 0x0400-0x3FFF */ + m_bank3->set_entry(0); /* RAM at 0x8000 */ + m_bank4->set_entry(3); /* ep2390 at 0xF000 */ + break; + } + + /* TODO: in real hardware the ENH bus line is pulled down + * until a I/O read is performed on a address with A0 address bit low and A1 or A2 address bit high + */ +} + +void z80ne_state::base_reset() +{ + for (int i = 0; i < LX383_KEYS; i++) + m_lx383_key[i] = 0xf0 | i; + m_lx383_scan_counter = 0x0f; + m_lx383_downsampler = LX383_DOWNSAMPLING; + + /* Initialize cassette interface */ + switch(m_io_lx_385->read() & 0x07) + { + case 0x01: + m_cass_data.speed = TAPE_300BPS; + m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 1600; + m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (2400 * 2); + m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (1200 * 2); + break; + case 0x02: + m_cass_data.speed = TAPE_600BPS; + m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 3200; + m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (4800 * 2); + m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (2400 * 2); + break; + case 0x04: + m_cass_data.speed = TAPE_1200BPS; + m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 6400; + m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (9600 * 2); + m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (4800 * 2); + } + m_cass_data.wave_length = m_cass_data.wave_short; + m_cass_data.output.length = m_cass_data.wave_length; + m_cass_data.output.level = 1; + m_cass_data.input.length = 0; + m_cass_data.input.bit = 1; + + m_uart->write_cs(0); + m_uart->write_nb1(1); + m_uart->write_nb2(1); + m_uart->write_tsb(1); + m_uart->write_eps(1); + m_uart->write_np(m_io_lx_385->read() & 0x80 ? 1 : 0); + m_uart->write_cs(1); + m_uart_clock->set_unscaled_clock(m_cass_data.speed * 16); + + lx385_ctrl_w(0); +} + +void z80ne_state::machine_reset() +{ + base_reset(); + + address_space &program = m_maincpu->space(AS_PROGRAM); + program.install_rom(0x0000, 0x03ff, m_rom); // do it here for F3 + m_rom_shadow_tap.remove(); + m_rom_shadow_tap = program.install_read_tap( + 0x8000, 0x83ff, + "rom_shadow_r", + [this] (offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + { + // delete this tap + m_rom_shadow_tap.remove(); + + // reinstall RAM over the ROM shadow + m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x03ff, m_mram); + } + }, + &m_rom_shadow_tap); +} + +void z80net_state::machine_reset() +{ + reset_lx387(); + z80ne_state::machine_reset(); +} + +void z80netb_state::machine_reset() +{ + base_reset(); + reset_lx387(); +} + +void z80netf_state::machine_reset() +{ + reset_lx390_banking(); + base_reset(); + reset_lx387(); + + // basic roms are exempt from memory tap + if ((m_io_config->read() & 0x07) != 2) + { + address_space &program = m_maincpu->space(AS_PROGRAM); + m_rom_shadow_tap.remove(); + m_rom_shadow_tap = program.install_read_tap( + 0x8000, 0xf3ff, + "rom_shadow_r", + [this] (offs_t offset, u8 &data, u8 mem_mask) + { + if (!machine().side_effects_disabled()) + { + // delete this tap + m_rom_shadow_tap.remove(); + + // reinstall RAM over the ROM shadow + m_bank1->set_entry(0); + } + }, + &m_rom_shadow_tap); + } +} + +INPUT_CHANGED_MEMBER(z80ne_state::z80ne_reset) +{ + uint8_t rst = m_io_rst->read(); + + if ( ! BIT(rst, 0)) + machine().schedule_soft_reset(); +} + +INPUT_CHANGED_MEMBER(z80net_state::z80net_nmi) +{ + uint8_t nmi = m_io_lx387_brk->read(); + + if ( ! BIT(nmi, 0)) + m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); +} + +void z80ne_state::machine_start() +{ + m_timer_nmi = timer_alloc(FUNC(z80ne_state::pulse_nmi), this); + + m_lx383_digits.resolve(); + + m_lx385_ctrl = 0x1f; + m_cassette_timer = timer_alloc(FUNC(z80ne_state::cassette_tc), this); + m_kbd_timer = timer_alloc(FUNC(z80ne_state::kbd_scan), this); + m_kbd_timer->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000)); +} + +void z80netf_state::machine_start() +{ + z80ne_state::machine_start(); + m_drv_led.resolve(); +} + + + +/****************************************************************************** + Drivers +******************************************************************************/ + +/* LX.383 - LX.384 HEX keyboard and display */ +uint8_t z80ne_state::lx383_r() +{ + /* + * Keyboard scanning + * + * IC14 NE555 astable oscillator + * IC13 74LS93 binary counter + * IC5 74LS240 tri-state buffer + * + * l'oscillatore NE555 alimenta il clock del contatore 74LS93 + * D0 - Q(A) --\ + * D1 - Q(B) |-- column + * D2 - Q(C) --/ + * D3 - Q(D) row + * D4 - CTRL + * D5 - 0 + * D6 - 0 + * D7 - ~KEY Pressed + */ + return m_lx383_key[m_lx383_scan_counter]; +} + +void z80ne_state::lx383_w(offs_t offset, uint8_t data) +{ + /* + * First 8 locations (F0-F7) are mapped to a dual-port 8-byte RAM + * The 1KHz NE-555 astable oscillator circuit drive + * a 4-bit 74LS93 binary counter. + * The 3 least significant bits of the counter are connected + * both to the read address of the dual-port ram and to + * a 74LS156 3 to 8 binary decoder driving the cathode + * of 8 7-segments LEDS. + * The data output of the dual-port ram drive the anodes + * of the LEDS through 74LS07 buffers. + * LED segments - dual-port RAM bit: + * A 0x01 + * B 0x02 + * C 0x04 + * D 0x08 + * E 0x10 + * F 0x20 + * G 0x40 + * P 0x80 (represented by DP in original schematics) + * + * A write in the range F0-FF starts a 74LS90 counter + * that trigger the NMI line of the CPU after 2 instruction + * fetch cycles for single step execution. + */ + + if ( offset < 8 ) + m_lx383_digits[offset] = data ^ 0xff; + else + { + // after writing to port 0xF8 and the first ~M1 cycles strike a NMI for single step execution + m_timer_nmi->adjust(m_maincpu->cycles_to_attotime(1)); + } +} + + +/* LX.385 Cassette tape interface */ +/* + * NE555 is connected to a 74LS93 binary counter + * 74LS93 output: + * QA-QC: column index for LEDs and keyboard + * QD: keyboard row select + * + * Port EE: UART Data Read/Write + * Port EF: Status/Control + * read, UART status bits read + * 0 OR Overrun + * 1 FE Framing Error + * 2 PE Parity Error + * 3 TBMT Transmitter Buffer Empty + * 4 DAV Data Available + * 5 EOC End Of Character + * 6 1 + * 7 1 + * write, UART control bits / Tape Unit select / Modulation control + * 0 bit1=0, bit0=0 UART Reset pulse + * 1 bit1=0, bit0=1 UART RDAV (Reset Data Available) pulse + * 2 Tape modulation enable + * 3 *TAPEA Enable (active low) (at reset: low) + * 4 *TAPEB Enable (active low) (at reset: low) + * Cassette is connected to the uart data input and output via the cassette + * interface hardware. + * + * The cassette interface hardware converts square-wave pulses into bits which the uart receives. + * + * 1. the cassette format: "frequency shift" is converted + into the uart data format "non-return to zero" + + 2. on cassette a 1 data bit is stored as 8 2400 Hz pulses + and a 0 data bit as 4 1200 Hz pulses + - At 1200 baud, a logic 1 is 1 cycle of 1200 Hz and a logic 0 is 1/2 cycle of 600 Hz. + - At 300 baud, a logic 1 is 8 cycles of 2400 Hz and a logic 0 is 4 cycles of 1200 Hz. + + Attenuation is applied to the signal and the square wave edges are rounded. + + A manchester encoder is used. A flip-flop synchronises input + data on the positive-edge of the clock pulse. + + The UART is a RCA CDP1854 CMOS device with pin 2 jumpered to GND to select the + AY-3-1015 compatibility mode. The jumper at P4 can be switched to place 12 V on + pin 2 for an old PMOS UART. + * + */ +uint8_t z80ne_state::lx385_ctrl_r() +{ + /* set unused bits high */ + uint8_t data = 0xc0; + + m_uart->write_swe(0); + data |= (m_uart->or_r( ) ? 0x01 : 0); + data |= (m_uart->fe_r( ) ? 0x02 : 0); + data |= (m_uart->pe_r( ) ? 0x04 : 0); + data |= (m_uart->tbmt_r() ? 0x08 : 0); + data |= (m_uart->dav_r( ) ? 0x10 : 0); + data |= (m_uart->eoc_r( ) ? 0x20 : 0); + m_uart->write_swe(1); + + return data; +} + +#define LX385_CASSETTE_MOTOR_MASK ((1<<3)|(1<<4)) + +void z80ne_state::lx385_ctrl_w(uint8_t data) +{ + /* Translate data to control signals + * 0 bit1=0, bit0=0 UART Reset pulse + * 1 bit1=0, bit0=1 UART RDAV (Reset Data Available) pulse + * 2 UART Tx Clock Enable (active high) + * 3 *TAPEA Enable (active low) (at reset: low) + * 4 *TAPEB Enable (active low) (at reset: low) + */ + uint8_t uart_reset, uart_rdav; + uint8_t motor_a, motor_b; + uint8_t changed_bits = (m_lx385_ctrl ^ data) & 0x1C; + m_lx385_ctrl = data; + + uart_reset = ((data & 0x03) == 0x00); + uart_rdav = ((data & 0x03) == 0x01); + motor_a = ((data & 0x08) == 0x00); + motor_b = ((data & 0x10) == 0x00); + + /* UART Reset and RDAV */ + if (uart_reset) + { + m_uart->write_xr(1); + m_uart->write_xr(0); + } + + if (uart_rdav) + { + m_uart->write_rdav(1); + m_uart->write_rdav(0); + } + + if (!changed_bits) return; + + /* motors */ + if(changed_bits & 0x18) + { + m_cassette1->change_state( + (motor_a) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); + + m_cassette2->change_state( + (motor_b) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); + + if (motor_a || motor_b) + m_cassette_timer->adjust(attotime::zero, 0, attotime::from_hz(LX385_TAPE_SAMPLE_FREQ)); + else + m_cassette_timer->adjust(attotime::zero); + } +} + +WRITE_LINE_MEMBER(z80ne_state::lx385_uart_tx_clock_w) +{ + if (BIT(m_lx385_ctrl, 2)) + m_uart->write_tcp(state); +} + +READ_LINE_MEMBER(z80net_state::lx387_shift_r) +{ + return BIT(m_io_modifiers->read(), 0) || BIT(m_io_modifiers->read(), 2); +} + +READ_LINE_MEMBER(z80net_state::lx387_control_r) +{ + return BIT(m_io_modifiers->read(), 1); +} + +uint8_t z80net_state::lx388_mc6847_videoram_r(offs_t offset) +{ + if (offset == ~0) return 0xff; + + int d6 = BIT(m_videoram[offset], 6); + int d7 = BIT(m_videoram[offset], 7); + + m_vdg->inv_w(d6 && d7); + m_vdg->as_w(!d6 && d7); + m_vdg->intext_w(!d6 && d7); + + return m_videoram[offset]; +} + +uint8_t z80net_state::lx387_data_r() +{ + uint8_t data = m_lx387_kr2376->data_r() & 0x7f; + data |= m_lx387_kr2376->get_output_pin(kr2376_device::KR2376_SO) << 7; + return data; +} + +uint8_t z80net_state::lx388_read_field_sync() +{ + return m_vdg->fs_r() << 7; +} + +/* + * DRQ INTRQ IC9B.10 IC8B.*Q + * 0 0 1 0 + * 0 1 0 x + * 1 0 0 x + * 1 1 0 x + * + */ + +void z80netf_state::lx390_motor_w(uint8_t data) +{ + /* Selection of drive and parameters + A write also causes the selected drive motor to turn on for about 3 seconds. + When the motor turns off, the drive is deselected. + d7 Unused (trs80: 1=MFM, 0=FM) + d6 (trs80: 1=Wait) + d5 0=Side 0, 1=Side 1 (trs80: 1=Write Precompensation enabled) + d4 Unused (trs80: 0=Side 0, 1=Side 1) + d3 1=select drive 3 + d2 1=select drive 2 + d1 1=select drive 1 + d0 1=select drive 0 */ + + floppy_image_device *floppy = nullptr; + + for (u8 f = 0; f < 4; f++) + if (BIT(data, f)) + floppy = m_floppy[f]->get_device(); + + m_wd1771->set_floppy(floppy); + + if (floppy) + { + floppy->ss_w(BIT(data, 5)); + floppy->mon_w(0); + } + + m_wd17xx_state.head = (data & 32) ? 1 : 0; + m_wd17xx_state.drive = data & 0x0F; + + /* no drive selected, turn off all leds */ + if (!m_wd17xx_state.drive) + { + m_drv_led[0] = 0; + m_drv_led[1] = 0; + } +} + +uint8_t z80netf_state::lx390_fdc_r(offs_t offset) +{ + uint8_t d; + + switch(offset) + { + case 0: + d = m_wd1771->status_r() ^ 0xff; + LOG("lx390_fdc_r, WD17xx status: %02x\n", d); + break; + case 1: + d = m_wd1771->track_r() ^ 0xff; + LOG("lx390_fdc_r, WD17xx track: %02x\n", d); + break; + case 2: + d = m_wd1771->sector_r() ^ 0xff; + LOG("lx390_fdc_r, WD17xx sector: %02x\n", d); + break; + case 3: + d = m_wd1771->data_r() ^ 0xff; + LOG("lx390_fdc_r, WD17xx data3: %02x\n", d); + break; + case 6: + d = 0xff; + m_bank1->set_entry(0); + break; + case 7: + d = m_wd1771->data_r() ^ 0xff; + LOG("lx390_fdc_r, WD17xx data7, force: %02x\n", d); + break; + default: + d = 0x00; + } + return d; +} + +void z80netf_state::lx390_fdc_w(offs_t offset, uint8_t data) +{ + uint8_t d = data; + switch(offset) + { + case 0: + LOG("lx390_fdc_w, WD17xx command: %02x\n", d); + m_wd1771->cmd_w(d ^ 0xff); + if (m_wd17xx_state.drive & 1) + m_drv_led[0] = 2; + else if (m_wd17xx_state.drive & 2) + m_drv_led[1] = 2; + break; + case 1: + LOG("lx390_fdc_w, WD17xx track: %02x\n", d); + m_wd1771->track_w(d ^ 0xff); + break; + case 2: + LOG("lx390_fdc_w, WD17xx sector: %02x\n", d); + m_wd1771->sector_w(d ^ 0xff); + break; + case 3: + m_wd1771->data_w(d ^ 0xff); + LOG("lx390_fdc_w, WD17xx data3: %02x\n", d); + break; + case 6: + LOG("lx390_fdc_w, motor_w: %02x\n", d); + lx390_motor_w(d); + break; + case 7: + LOG("lx390_fdc_w, WD17xx data7, force: %02x\n", d); + m_wd1771->data_w(d ^ 0xff); + break; + } +} + + + +/****************************************************************************** + Memory Maps +******************************************************************************/ /* LX.382 CPU Board RAM */ /* LX.382 CPU Board EPROM */ @@ -221,7 +1137,6 @@ void z80netf_state::io_map(address_map &map) Input Ports ******************************************************************************/ - static INPUT_PORTS_START( z80ne ) /* LX.384 Hex Keyboard and Display */ /* @@ -269,12 +1184,10 @@ static INPUT_PORTS_START( z80ne ) PORT_CONFNAME( 0x08, 0x00, "LX.385: P4 Parity Check") PORT_CONFSETTING( 0x00, "Parity Check Enabled") PORT_CONFSETTING( 0x08, "Parity Check Disabled") - INPUT_PORTS_END static INPUT_PORTS_START( z80net ) - PORT_INCLUDE( z80ne ) /* LX.387 Keyboard BREAK key */ @@ -282,7 +1195,6 @@ static INPUT_PORTS_START( z80net ) PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("Break") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END)) PORT_CHANGED_MEMBER(DEVICE_SELF, z80net_state, z80net_nmi, 0) /* LX.387 Keyboard (Encoded by KR2376) */ - PORT_START("X0") PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_UNUSED ) @@ -389,13 +1301,11 @@ static INPUT_PORTS_START( z80net ) PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL) PORT_CODE(KEYCODE_RCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL)) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Shift Lock") PORT_CODE(KEYCODE_CAPSLOCK) PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_TOGGLE - INPUT_PORTS_END static INPUT_PORTS_START( z80netf ) - -PORT_INCLUDE( z80net ) + PORT_INCLUDE( z80net ) /* Settings */ PORT_START("CONFIG") @@ -406,15 +1316,14 @@ PORT_INCLUDE( z80net ) PORT_CONFSETTING( 0x04, "EP1390 Boot Loader for NE DOS 1.0/1.5") PORT_CONFSETTING( 0x05, "EP2390 Boot Loader for NE DOS G.1") PORT_BIT(0xf8, 0xf8, IPT_UNUSED) - INPUT_PORTS_END - /****************************************************************************** Machine Drivers ******************************************************************************/ + #if 0 static const uint32_t lx388palette[] = { @@ -612,11 +1521,12 @@ void z80netf_state::z80netf(machine_config &config) SOFTWARE_LIST(config, "flop_list").set_original("z80ne_flop"); } + + /****************************************************************************** ROM Definitions ******************************************************************************/ - ROM_START( z80ne ) ROM_REGION(0x0400, "maincpu", 0) ROM_LOAD( "ep382.ic5", 0x0000, 0x0400, CRC(55818366) SHA1(adcac04b83c09265517b7bafbc2f5f665d751bec) ) @@ -628,10 +1538,8 @@ ROM_START( z80net ) ROM_END ROM_START( z80netb ) -/* - * 16k Basic - */ ROM_REGION(0x4000, "maincpu", 0) + // 16k Basic ROM_LOAD( "548-1.ic1", 0x0000, 0x0800, CRC(868cad39) SHA1(0ea8af010786a080f823a879a4211f5712d260da) ) ROM_LOAD( "548-2.ic2", 0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0) ) ROM_LOAD( "548-3.ic3", 0x1000, 0x0800, CRC(9c1fe511) SHA1(ff5b6e49a137c2ff9cb760c39bfd85ce4b52bb7d) ) @@ -644,7 +1552,7 @@ ROM_END ROM_START( z80netf ) ROM_REGION(0x5000, "maincpu", 0) - /* ep548 banked at 0x0000 - 0x3FFF */ + // ep548 banked at 0x0000 - 0x3FFF ROM_LOAD( "548-1.ic1", 0x0000, 0x0800, CRC(868cad39) SHA1(0ea8af010786a080f823a879a4211f5712d260da) ) ROM_LOAD( "548-2.ic2", 0x0800, 0x0800, CRC(ac297d99) SHA1(ccf31d3f9d02c3b68a0ee3be4984424df0e83ab0) ) ROM_LOAD( "548-3.ic3", 0x1000, 0x0800, CRC(9c1fe511) SHA1(ff5b6e49a137c2ff9cb760c39bfd85ce4b52bb7d) ) @@ -654,16 +1562,19 @@ ROM_START( z80netf ) ROM_LOAD( "548-7.ic7", 0x3000, 0x0800, CRC(0bab06c0) SHA1(d52f1519c798e91f25648e996b1db174d90ce0f5) ) ROM_LOAD( "548-8.ic8", 0x3800, 0x0800, CRC(f381b594) SHA1(2de7a8941ba48d463974c73d62e994d3cbe2868d) ) - /* ep382 - 8000 */ + // ep382 - 8000 ROM_LOAD( "ep382.ic5", 0x4000, 0x0400, CRC(55818366) SHA1(adcac04b83c09265517b7bafbc2f5f665d751bec) ) - /* ep390 - F000 */ + // ep390 - F000 ROM_LOAD( "ep390.ic6", 0x4400, 0x0400, CRC(e4dd7de9) SHA1(523caa97112a9e67cc078c1a70ceee94ec232093) ) - /* ep1390 - F000 */ + // ep1390 - F000 ROM_LOAD( "ep1390.ic6", 0x4800, 0x0400, CRC(dc2cbc1d) SHA1(e23418b8f8261a17892f3a73ec09c72bb02e1d0b) ) - /* ep2390 - F000 */ - ROM_LOAD( "ep2390.ic6", 0x4C00, 0x0400, CRC(28d28eee) SHA1(b80f75c1ac4905ae369ecbc9b9ce120cc85502ed) ) + // ep2390 - F000 + ROM_LOAD( "ep2390.ic6", 0x4c00, 0x0400, CRC(28d28eee) SHA1(b80f75c1ac4905ae369ecbc9b9ce120cc85502ed) ) ROM_END +} // anonymous namespace + + // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS COMP( 1980, z80ne, 0, 0, z80ne, z80ne, z80ne_state, init_z80ne, "Nuova Elettronica", "Z80NE", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE ) COMP( 1980, z80net, z80ne, 0, z80net, z80net, z80net_state, init_z80ne, "Nuova Elettronica", "Z80NE + LX.388", MACHINE_NO_SOUND_HW | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/ne/z80ne.h b/src/mame/ne/z80ne.h deleted file mode 100644 index 30c6403d386..00000000000 --- a/src/mame/ne/z80ne.h +++ /dev/null @@ -1,252 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Roberto Lavarone -/***************************************************************************** - * - * includes/z80ne.h - * - * Nuova Elettronica Z80NE - * - * http://www.z80ne.com/ - * - ****************************************************************************/ - -#ifndef MAME_INCLUDES_Z80NE_H -#define MAME_INCLUDES_Z80NE_H - -#pragma once - -#include "imagedev/cassette.h" -#include "imagedev/floppy.h" -#include "machine/ay31015.h" -#include "machine/clock.h" -#include "machine/kr2376.h" -#include "machine/ram.h" -#include "machine/wd_fdc.h" -#include "video/mc6847.h" - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define Z80NE_CPU_SPEED_HZ 1920000 /* 1.92 MHz */ - -#define LX383_KEYS 16 -#define LX383_DOWNSAMPLING 16 - -#define LX385_TAPE_SAMPLE_FREQ 38400 - -/* wave duration threshold */ -enum z80netape_speed -{ - TAPE_300BPS = 300, /* 300 bps */ - TAPE_600BPS = 600, /* 600 bps */ - TAPE_1200BPS = 1200 /* 1200 bps */ -}; - -struct z80ne_cass_data_t { - struct { - int length = 0; /* time cassette level is at input.level */ - int level = 0; /* cassette level */ - int bit = 0; /* bit being read */ - } input; - struct { - int length = 0; /* time cassette level is at output.level */ - int level = 0; /* cassette level */ - int bit = 0; /* bit to output */ - } output; - z80netape_speed speed; /* 300 - 600 - 1200 */ - int wave_filter = 0; - int wave_length = 0; - int wave_short = 0; - int wave_long = 0; -}; - - -class z80ne_state : public driver_device -{ -public: - z80ne_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig, type, tag), - m_uart(*this, "uart"), - m_uart_clock(*this, "uart_clock"), - m_maincpu(*this, "maincpu"), - m_cassette1(*this, "cassette"), - m_cassette2(*this, "cassette2"), - m_ram(*this, RAM_TAG), - m_bank1(*this, "bank1"), - m_bank2(*this, "bank2"), - m_bank3(*this, "bank3"), - m_bank4(*this, "bank4"), - m_io_row0(*this, "ROW0"), - m_io_row1(*this, "ROW1"), - m_io_ctrl(*this, "CTRL"), - m_io_rst(*this, "RST"), - m_io_lx_385(*this, "LX.385"), - m_lx383_digits(*this, "digit%u", 0U) - , m_rom(*this, "maincpu") - , m_mram(*this, "mainram") - { } - - void z80ne(machine_config &config); - void init_z80ne(); - - DECLARE_INPUT_CHANGED_MEMBER(z80ne_reset); - -protected: - virtual void machine_start() override; - virtual void machine_reset() override; - - void base_reset(); - void save_state_vars(); - - static void floppy_formats(format_registration &fr); - - uint8_t m_lx383_scan_counter = 0; - uint8_t m_lx383_key[LX383_KEYS]{}; - int m_lx383_downsampler = 0; - uint8_t m_lx385_ctrl = 0; - emu_timer *m_cassette_timer = nullptr; - emu_timer *m_kbd_timer = nullptr; - z80ne_cass_data_t m_cass_data; - - uint8_t lx383_r(); - void lx383_w(offs_t offset, uint8_t data); - uint8_t lx385_ctrl_r(); - void lx385_ctrl_w(uint8_t data); - DECLARE_WRITE_LINE_MEMBER(lx385_uart_tx_clock_w); - - TIMER_CALLBACK_MEMBER(cassette_tc); - TIMER_CALLBACK_MEMBER(kbd_scan); - TIMER_CALLBACK_MEMBER(pulse_nmi); - - memory_passthrough_handler m_rom_shadow_tap; - required_device m_uart; - required_device m_uart_clock; - required_device m_maincpu; - required_device m_cassette1; - required_device m_cassette2; - optional_device m_ram; - optional_memory_bank m_bank1; - optional_memory_bank m_bank2; - optional_memory_bank m_bank3; - optional_memory_bank m_bank4; - required_ioport m_io_row0; - required_ioport m_io_row1; - required_ioport m_io_ctrl; - required_ioport m_io_rst; - required_ioport m_io_lx_385; - output_finder<8> m_lx383_digits; - required_region_ptr m_rom; - optional_shared_ptr m_mram; - - emu_timer *m_timer_nmi = nullptr; - - cassette_image_device *cassette_device_image(); - -private: - void mem_map(address_map &map); - void io_map(address_map &map); -}; - -class z80net_state : public z80ne_state -{ -public: - z80net_state(const machine_config &mconfig, device_type type, const char *tag) - : z80ne_state(mconfig, type, tag), - m_videoram(*this, "videoram"), - m_vdg(*this, "mc6847"), - m_lx387_kr2376(*this, "lx387_kr2376"), - m_io_lx387_brk(*this, "LX387_BRK"), - m_io_modifiers(*this, "MODIFIERS") - { - } - - void lx387(machine_config &config); - void z80net(machine_config &config); - - DECLARE_INPUT_CHANGED_MEMBER(z80net_nmi); - -protected: - virtual void machine_reset() override; - - DECLARE_READ_LINE_MEMBER(lx387_shift_r); - DECLARE_READ_LINE_MEMBER(lx387_control_r); - uint8_t lx387_data_r(); - uint8_t lx388_mc6847_videoram_r(offs_t offset); - uint8_t lx388_read_field_sync(); - - required_shared_ptr m_videoram; - required_device m_vdg; - required_device m_lx387_kr2376; - required_ioport m_io_lx387_brk; - required_ioport m_io_modifiers; - - void reset_lx387(); - - void io_map(address_map &map); - -private: - void mem_map(address_map &map); -}; - -class z80netb_state : public z80net_state -{ -public: - z80netb_state(const machine_config &mconfig, device_type type, const char *tag) - : z80net_state(mconfig, type, tag) - { - } - - void z80netb(machine_config &config); - -protected: - virtual void machine_reset() override; - -private: - void mem_map(address_map &map); -}; - -class z80netf_state : public z80netb_state -{ -public: - z80netf_state(const machine_config &mconfig, device_type type, const char *tag) - : z80netb_state(mconfig, type, tag), - m_io_config(*this, "CONFIG"), - m_floppy(*this, "wd1771:%u", 0U), - m_wd1771(*this, "wd1771"), - m_drv_led(*this, "drv%u", 0U) - { - } - - void z80netf(machine_config &config); - -private: - virtual void machine_start() override; - virtual void machine_reset() override; - virtual void driver_start() override; - - struct wd17xx_state_t - { - int drq = 0; - int intrq = 0; - uint8_t drive = 0; /* current drive */ - uint8_t head = 0; /* current head */ - }; - - void mem_map(address_map &map); - void io_map(address_map &map); - - void lx390_motor_w(uint8_t data); - uint8_t lx390_fdc_r(offs_t offset); - void lx390_fdc_w(offs_t offset, uint8_t data); - - void reset_lx390_banking(); - - required_ioport m_io_config; - required_device_array m_floppy; - required_device m_wd1771; - wd17xx_state_t m_wd17xx_state; - output_finder<2> m_drv_led; -}; - -#endif // MAME_INCLUDES_Z80NE_H diff --git a/src/mame/ne/z80ne_m.cpp b/src/mame/ne/z80ne_m.cpp deleted file mode 100644 index bf2a96b250b..00000000000 --- a/src/mame/ne/z80ne_m.cpp +++ /dev/null @@ -1,694 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Roberto Lavarone -/*********************************************************************** - - machine/z80ne.c - - Functions to emulate general aspects of the machine (RAM, ROM, - interrupts, I/O ports) - -***********************************************************************/ - -/* Core includes */ -#include "emu.h" -#include "z80ne.h" - -//#define VERBOSE 1 -#include "logmacro.h" - - - - - -/* timer to read cassette waveforms */ - -cassette_image_device* z80ne_state::cassette_device_image() -{ - if (m_lx385_ctrl & 0x08) - return m_cassette2; - else - return m_cassette1; -} - -TIMER_CALLBACK_MEMBER(z80ne_state::cassette_tc) -{ - uint8_t cass_ws = 0; - m_cass_data.input.length++; - - cass_ws = ((cassette_device_image())->input() > +0.02) ? 1 : 0; - - if ((cass_ws ^ m_cass_data.input.level) & cass_ws) - { - m_cass_data.input.level = cass_ws; - m_cass_data.input.bit = ((m_cass_data.input.length < m_cass_data.wave_filter) || (m_cass_data.input.length > 0x20)) ? 1 : 0; - m_cass_data.input.length = 0; - m_uart->write_si(m_cass_data.input.bit); - } - m_cass_data.input.level = cass_ws; - - /* saving a tape - convert the serial stream from the uart */ - - m_cass_data.output.length--; - - if (!(m_cass_data.output.length)) - { - if (m_cass_data.output.level) - m_cass_data.output.level = 0; - else - { - m_cass_data.output.level=1; - cass_ws = m_uart->so_r(); - m_cass_data.wave_length = cass_ws ? m_cass_data.wave_short : m_cass_data.wave_long; - } - cassette_device_image()->output(m_cass_data.output.level ? -1.0 : +1.0); - m_cass_data.output.length = m_cass_data.wave_length; - } -} - -void z80ne_state::save_state_vars() -{ - save_item(NAME(m_lx383_scan_counter)); - save_item(NAME(m_lx383_key)); - save_item(NAME(m_lx383_downsampler)); - save_item(NAME(m_lx385_ctrl)); -} - -void z80ne_state::init_z80ne() -{ - save_state_vars(); -} - -void z80netf_state::driver_start() -{ - save_state_vars(); - - /* first two entries point to rom on reset */ - u8 *r = m_ram->pointer(); - m_bank1->configure_entry(0, r); /* RAM at 0x0000-0x03FF */ - m_bank1->configure_entries(1, 3, m_rom+0x4400, 0x0400); /* ep390, ep1390, ep2390 at 0x0000-0x03FF */ - m_bank1->configure_entry(4, m_rom+0x4000); /* ep382 at 0x0000-0x03FF */ - m_bank1->configure_entry(5, m_rom); /* ep548 at 0x0000-0x03FF */ - - m_bank2->configure_entry(0, r+0x0400); /* RAM at 0x0400 */ - m_bank2->configure_entry(1, m_rom+0x0400); /* ep548 at 0x0400-0x3FFF */ - - m_bank3->configure_entry(0, r+0x4000); /* RAM at 0x8000 */ - m_bank3->configure_entry(1, m_rom+0x4000); /* ep382 at 0x8000 */ - - m_bank4->configure_entry(0, r+0x5000); /* RAM at 0xF000 */ - m_bank4->configure_entries(1, 3, m_rom+0x4400, 0x0400); /* ep390, ep1390, ep2390 at 0xF000 */ - -} - -TIMER_CALLBACK_MEMBER(z80ne_state::kbd_scan) -{ - /* - * NE555 is connected to a 74LS93 binary counter - * 74LS93 output: - * QA-QC: column index for LEDs and keyboard - * QD: keyboard row select - * - * Port F0 input bit assignment: - * 0 QA bits 0..3 of row counter - * 1 QB - * 2 QC - * 3 QD - * 4 Control button pressed, active high - * 5 Always low - * 6 Always low - * 7 Selected button pressed, active low - * - * - */ - - uint16_t key_bits; - uint8_t ctrl; //, rst; - uint8_t i; - - /* 4-bit counter */ - --m_lx383_scan_counter; - m_lx383_scan_counter &= 0x0f; - - if ( --m_lx383_downsampler == 0 ) - { - m_lx383_downsampler = LX383_DOWNSAMPLING; - key_bits = (m_io_row1->read() << 8) | m_io_row0->read(); -// rst = m_io_rst->read(); - ctrl = m_io_ctrl->read(); - - for ( i = 0; i>= 1; - } - } -} - -TIMER_CALLBACK_MEMBER(z80ne_state::pulse_nmi) -{ - m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); -} - -void z80net_state::reset_lx387() -{ - m_lx387_kr2376->set_input_pin( kr2376_device::KR2376_DSII, 0); - m_lx387_kr2376->set_input_pin( kr2376_device::KR2376_PII, 0); -} - -void z80netf_state::reset_lx390_banking() -{ - switch (m_io_config->read() & 0x07) - { - case 0x01: /* EP382 Hex Monitor */ - if (VERBOSE) - logerror("reset_lx390_banking: banking ep382\n"); - m_bank1->set_entry(4); /* ep382 at 0x0000 for 3 cycles, then RAM */ - m_bank2->set_entry(0); /* RAM at 0x0400 */ - m_bank3->set_entry(1); /* ep382 at 0x8000 */ - m_bank4->set_entry(0); /* RAM at 0xF000 */ - break; - case 0x02: /* EP548 16k BASIC */ - if (VERBOSE) - logerror("reset_lx390_banking: banking ep548\n"); - m_bank1->set_entry(5); /* ep548 at 0x0000-0x03FF */ - m_bank2->set_entry(1); /* ep548 at 0x0400-0x3FFF */ - m_bank3->set_entry(0); /* RAM at 0x8000 */ - m_bank4->set_entry(0); /* RAM at 0xF000 */ - break; - case 0x03: /* EP390 Boot Loader for 5.5k floppy BASIC */ - if (VERBOSE) - logerror("reset_lx390_banking: banking ep390\n"); - m_bank1->set_entry(1); /* ep390 at 0x0000-0 x03FF for 3 cycles, then RAM */ - m_bank2->set_entry(0); /* RAM at 0x0400-0x3FFF */ - m_bank3->set_entry(0); /* RAM at 0x8000 */ - m_bank4->set_entry(1); /* ep390 at 0xF000 */ - break; - case 0x04: /* EP1390 Boot Loader for NE DOS 1.0/1.5 */ - if (VERBOSE) - logerror("reset_lx390_banking: banking ep1390\n"); - m_bank1->set_entry(2); /* ep1390 at 0x0000-0x03FF for 3 cycles, then RAM */ - m_bank2->set_entry(0); /* RAM at 0x0400-0x3FFF */ - m_bank3->set_entry(0); /* RAM at 0x8000 */ - m_bank4->set_entry(2); /* ep1390 at 0xF000 */ - break; - case 0x05: /* EP2390 Boot Loader for NE DOS G.1 */ - if (VERBOSE) - logerror("reset_lx390_banking: banking ep2390\n"); - m_bank1->set_entry(3); /* ep2390 at 0x0000-0x03FF for 3 cycles, then RAM */ - m_bank2->set_entry(0); /* RAM at 0x0400-0x3FFF */ - m_bank3->set_entry(0); /* RAM at 0x8000 */ - m_bank4->set_entry(3); /* ep2390 at 0xF000 */ - break; - } - - /* TODO: in real hardware the ENH bus line is pulled down - * until a I/O read is performed on a address with A0 address bit low and A1 or A2 address bit high - */ -} - -void z80ne_state::base_reset() -{ - for (int i = 0; i < LX383_KEYS; i++) - m_lx383_key[i] = 0xf0 | i; - m_lx383_scan_counter = 0x0f; - m_lx383_downsampler = LX383_DOWNSAMPLING; - - /* Initialize cassette interface */ - switch(m_io_lx_385->read() & 0x07) - { - case 0x01: - m_cass_data.speed = TAPE_300BPS; - m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 1600; - m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (2400 * 2); - m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (1200 * 2); - break; - case 0x02: - m_cass_data.speed = TAPE_600BPS; - m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 3200; - m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (4800 * 2); - m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (2400 * 2); - break; - case 0x04: - m_cass_data.speed = TAPE_1200BPS; - m_cass_data.wave_filter = LX385_TAPE_SAMPLE_FREQ / 6400; - m_cass_data.wave_short = LX385_TAPE_SAMPLE_FREQ / (9600 * 2); - m_cass_data.wave_long = LX385_TAPE_SAMPLE_FREQ / (4800 * 2); - } - m_cass_data.wave_length = m_cass_data.wave_short; - m_cass_data.output.length = m_cass_data.wave_length; - m_cass_data.output.level = 1; - m_cass_data.input.length = 0; - m_cass_data.input.bit = 1; - - m_uart->write_cs(0); - m_uart->write_nb1(1); - m_uart->write_nb2(1); - m_uart->write_tsb(1); - m_uart->write_eps(1); - m_uart->write_np(m_io_lx_385->read() & 0x80 ? 1 : 0); - m_uart->write_cs(1); - m_uart_clock->set_unscaled_clock(m_cass_data.speed * 16); - - lx385_ctrl_w(0); -} - -void z80ne_state::machine_reset() -{ - base_reset(); - - address_space &program = m_maincpu->space(AS_PROGRAM); - program.install_rom(0x0000, 0x03ff, m_rom); // do it here for F3 - m_rom_shadow_tap.remove(); - m_rom_shadow_tap = program.install_read_tap( - 0x8000, 0x83ff, - "rom_shadow_r", - [this] (offs_t offset, u8 &data, u8 mem_mask) - { - if (!machine().side_effects_disabled()) - { - // delete this tap - m_rom_shadow_tap.remove(); - - // reinstall RAM over the ROM shadow - m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x03ff, m_mram); - } - }, - &m_rom_shadow_tap); -} - -void z80net_state::machine_reset() -{ - reset_lx387(); - z80ne_state::machine_reset(); -} - -void z80netb_state::machine_reset() -{ - base_reset(); - reset_lx387(); -} - -void z80netf_state::machine_reset() -{ - reset_lx390_banking(); - base_reset(); - reset_lx387(); - - // basic roms are exempt from memory tap - if ((m_io_config->read() & 0x07) != 2) - { - address_space &program = m_maincpu->space(AS_PROGRAM); - m_rom_shadow_tap.remove(); - m_rom_shadow_tap = program.install_read_tap( - 0x8000, 0xf3ff, - "rom_shadow_r", - [this] (offs_t offset, u8 &data, u8 mem_mask) - { - if (!machine().side_effects_disabled()) - { - // delete this tap - m_rom_shadow_tap.remove(); - - // reinstall RAM over the ROM shadow - m_bank1->set_entry(0); - } - }, - &m_rom_shadow_tap); - } -} - -INPUT_CHANGED_MEMBER(z80ne_state::z80ne_reset) -{ - uint8_t rst = m_io_rst->read(); - - if ( ! BIT(rst, 0)) - machine().schedule_soft_reset(); -} - -INPUT_CHANGED_MEMBER(z80net_state::z80net_nmi) -{ - uint8_t nmi = m_io_lx387_brk->read(); - - if ( ! BIT(nmi, 0)) - m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); -} - -void z80ne_state::machine_start() -{ - m_timer_nmi = timer_alloc(FUNC(z80ne_state::pulse_nmi), this); - - m_lx383_digits.resolve(); - - m_lx385_ctrl = 0x1f; - m_cassette_timer = timer_alloc(FUNC(z80ne_state::cassette_tc), this); - m_kbd_timer = timer_alloc(FUNC(z80ne_state::kbd_scan), this); - m_kbd_timer->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000)); -} - -void z80netf_state::machine_start() -{ - z80ne_state::machine_start(); - m_drv_led.resolve(); -} - -/****************************************************************************** - Drivers -******************************************************************************/ - -/* LX.383 - LX.384 HEX keyboard and display */ -uint8_t z80ne_state::lx383_r() -{ - /* - * Keyboard scanning - * - * IC14 NE555 astable oscillator - * IC13 74LS93 binary counter - * IC5 74LS240 tri-state buffer - * - * l'oscillatore NE555 alimenta il clock del contatore 74LS93 - * D0 - Q(A) --\ - * D1 - Q(B) |-- column - * D2 - Q(C) --/ - * D3 - Q(D) row - * D4 - CTRL - * D5 - 0 - * D6 - 0 - * D7 - ~KEY Pressed - */ - return m_lx383_key[m_lx383_scan_counter]; -} - -void z80ne_state::lx383_w(offs_t offset, uint8_t data) -{ - /* - * First 8 locations (F0-F7) are mapped to a dual-port 8-byte RAM - * The 1KHz NE-555 astable oscillator circuit drive - * a 4-bit 74LS93 binary counter. - * The 3 least significant bits of the counter are connected - * both to the read address of the dual-port ram and to - * a 74LS156 3 to 8 binary decoder driving the cathode - * of 8 7-segments LEDS. - * The data output of the dual-port ram drive the anodes - * of the LEDS through 74LS07 buffers. - * LED segments - dual-port RAM bit: - * A 0x01 - * B 0x02 - * C 0x04 - * D 0x08 - * E 0x10 - * F 0x20 - * G 0x40 - * P 0x80 (represented by DP in original schematics) - * - * A write in the range F0-FF starts a 74LS90 counter - * that trigger the NMI line of the CPU after 2 instruction - * fetch cycles for single step execution. - */ - - if ( offset < 8 ) - m_lx383_digits[offset] = data ^ 0xff; - else - { - // after writing to port 0xF8 and the first ~M1 cycles strike a NMI for single step execution - m_timer_nmi->adjust(m_maincpu->cycles_to_attotime(1)); - } -} - - -/* LX.385 Cassette tape interface */ -/* - * NE555 is connected to a 74LS93 binary counter - * 74LS93 output: - * QA-QC: column index for LEDs and keyboard - * QD: keyboard row select - * - * Port EE: UART Data Read/Write - * Port EF: Status/Control - * read, UART status bits read - * 0 OR Overrun - * 1 FE Framing Error - * 2 PE Parity Error - * 3 TBMT Transmitter Buffer Empty - * 4 DAV Data Available - * 5 EOC End Of Character - * 6 1 - * 7 1 - * write, UART control bits / Tape Unit select / Modulation control - * 0 bit1=0, bit0=0 UART Reset pulse - * 1 bit1=0, bit0=1 UART RDAV (Reset Data Available) pulse - * 2 Tape modulation enable - * 3 *TAPEA Enable (active low) (at reset: low) - * 4 *TAPEB Enable (active low) (at reset: low) - * Cassette is connected to the uart data input and output via the cassette - * interface hardware. - * - * The cassette interface hardware converts square-wave pulses into bits which the uart receives. - * - * 1. the cassette format: "frequency shift" is converted - into the uart data format "non-return to zero" - - 2. on cassette a 1 data bit is stored as 8 2400 Hz pulses - and a 0 data bit as 4 1200 Hz pulses - - At 1200 baud, a logic 1 is 1 cycle of 1200 Hz and a logic 0 is 1/2 cycle of 600 Hz. - - At 300 baud, a logic 1 is 8 cycles of 2400 Hz and a logic 0 is 4 cycles of 1200 Hz. - - Attenuation is applied to the signal and the square wave edges are rounded. - - A manchester encoder is used. A flip-flop synchronises input - data on the positive-edge of the clock pulse. - - The UART is a RCA CDP1854 CMOS device with pin 2 jumpered to GND to select the - AY-3-1015 compatibility mode. The jumper at P4 can be switched to place 12 V on - pin 2 for an old PMOS UART. - * - */ -uint8_t z80ne_state::lx385_ctrl_r() -{ - /* set unused bits high */ - uint8_t data = 0xc0; - - m_uart->write_swe(0); - data |= (m_uart->or_r( ) ? 0x01 : 0); - data |= (m_uart->fe_r( ) ? 0x02 : 0); - data |= (m_uart->pe_r( ) ? 0x04 : 0); - data |= (m_uart->tbmt_r() ? 0x08 : 0); - data |= (m_uart->dav_r( ) ? 0x10 : 0); - data |= (m_uart->eoc_r( ) ? 0x20 : 0); - m_uart->write_swe(1); - - return data; -} - -#define LX385_CASSETTE_MOTOR_MASK ((1<<3)|(1<<4)) - -void z80ne_state::lx385_ctrl_w(uint8_t data) -{ - /* Translate data to control signals - * 0 bit1=0, bit0=0 UART Reset pulse - * 1 bit1=0, bit0=1 UART RDAV (Reset Data Available) pulse - * 2 UART Tx Clock Enable (active high) - * 3 *TAPEA Enable (active low) (at reset: low) - * 4 *TAPEB Enable (active low) (at reset: low) - */ - uint8_t uart_reset, uart_rdav; - uint8_t motor_a, motor_b; - uint8_t changed_bits = (m_lx385_ctrl ^ data) & 0x1C; - m_lx385_ctrl = data; - - uart_reset = ((data & 0x03) == 0x00); - uart_rdav = ((data & 0x03) == 0x01); - motor_a = ((data & 0x08) == 0x00); - motor_b = ((data & 0x10) == 0x00); - - /* UART Reset and RDAV */ - if (uart_reset) - { - m_uart->write_xr(1); - m_uart->write_xr(0); - } - - if (uart_rdav) - { - m_uart->write_rdav(1); - m_uart->write_rdav(0); - } - - if (!changed_bits) return; - - /* motors */ - if(changed_bits & 0x18) - { - m_cassette1->change_state( - (motor_a) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); - - m_cassette2->change_state( - (motor_b) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); - - if (motor_a || motor_b) - m_cassette_timer->adjust(attotime::zero, 0, attotime::from_hz(LX385_TAPE_SAMPLE_FREQ)); - else - m_cassette_timer->adjust(attotime::zero); - } -} - -WRITE_LINE_MEMBER(z80ne_state::lx385_uart_tx_clock_w) -{ - if (BIT(m_lx385_ctrl, 2)) - m_uart->write_tcp(state); -} - -READ_LINE_MEMBER(z80net_state::lx387_shift_r) -{ - return BIT(m_io_modifiers->read(), 0) || BIT(m_io_modifiers->read(), 2); -} - -READ_LINE_MEMBER(z80net_state::lx387_control_r) -{ - return BIT(m_io_modifiers->read(), 1); -} - -uint8_t z80net_state::lx388_mc6847_videoram_r(offs_t offset) -{ - if (offset == ~0) return 0xff; - - int d6 = BIT(m_videoram[offset], 6); - int d7 = BIT(m_videoram[offset], 7); - - m_vdg->inv_w(d6 && d7); - m_vdg->as_w(!d6 && d7); - m_vdg->intext_w(!d6 && d7); - - return m_videoram[offset]; -} - -uint8_t z80net_state::lx387_data_r() -{ - uint8_t data = m_lx387_kr2376->data_r() & 0x7f; - data |= m_lx387_kr2376->get_output_pin(kr2376_device::KR2376_SO) << 7; - return data; -} - -uint8_t z80net_state::lx388_read_field_sync() -{ - return m_vdg->fs_r() << 7; -} - -/* - * DRQ INTRQ IC9B.10 IC8B.*Q - * 0 0 1 0 - * 0 1 0 x - * 1 0 0 x - * 1 1 0 x - * - */ - -void z80netf_state::lx390_motor_w(uint8_t data) -{ - /* Selection of drive and parameters - A write also causes the selected drive motor to turn on for about 3 seconds. - When the motor turns off, the drive is deselected. - d7 Unused (trs80: 1=MFM, 0=FM) - d6 (trs80: 1=Wait) - d5 0=Side 0, 1=Side 1 (trs80: 1=Write Precompensation enabled) - d4 Unused (trs80: 0=Side 0, 1=Side 1) - d3 1=select drive 3 - d2 1=select drive 2 - d1 1=select drive 1 - d0 1=select drive 0 */ - - floppy_image_device *floppy = nullptr; - - for (u8 f = 0; f < 4; f++) - if (BIT(data, f)) - floppy = m_floppy[f]->get_device(); - - m_wd1771->set_floppy(floppy); - - if (floppy) - { - floppy->ss_w(BIT(data, 5)); - floppy->mon_w(0); - } - - m_wd17xx_state.head = (data & 32) ? 1 : 0; - m_wd17xx_state.drive = data & 0x0F; - - /* no drive selected, turn off all leds */ - if (!m_wd17xx_state.drive) - { - m_drv_led[0] = 0; - m_drv_led[1] = 0; - } -} - -uint8_t z80netf_state::lx390_fdc_r(offs_t offset) -{ - uint8_t d; - - switch(offset) - { - case 0: - d = m_wd1771->status_r() ^ 0xff; - LOG("lx390_fdc_r, WD17xx status: %02x\n", d); - break; - case 1: - d = m_wd1771->track_r() ^ 0xff; - LOG("lx390_fdc_r, WD17xx track: %02x\n", d); - break; - case 2: - d = m_wd1771->sector_r() ^ 0xff; - LOG("lx390_fdc_r, WD17xx sector: %02x\n", d); - break; - case 3: - d = m_wd1771->data_r() ^ 0xff; - LOG("lx390_fdc_r, WD17xx data3: %02x\n", d); - break; - case 6: - d = 0xff; - m_bank1->set_entry(0); - break; - case 7: - d = m_wd1771->data_r() ^ 0xff; - LOG("lx390_fdc_r, WD17xx data7, force: %02x\n", d); - break; - default: - d = 0x00; - } - return d; -} - -void z80netf_state::lx390_fdc_w(offs_t offset, uint8_t data) -{ - uint8_t d = data; - switch(offset) - { - case 0: - LOG("lx390_fdc_w, WD17xx command: %02x\n", d); - m_wd1771->cmd_w(d ^ 0xff); - if (m_wd17xx_state.drive & 1) - m_drv_led[0] = 2; - else if (m_wd17xx_state.drive & 2) - m_drv_led[1] = 2; - break; - case 1: - LOG("lx390_fdc_w, WD17xx track: %02x\n", d); - m_wd1771->track_w(d ^ 0xff); - break; - case 2: - LOG("lx390_fdc_w, WD17xx sector: %02x\n", d); - m_wd1771->sector_w(d ^ 0xff); - break; - case 3: - m_wd1771->data_w(d ^ 0xff); - LOG("lx390_fdc_w, WD17xx data3: %02x\n", d); - break; - case 6: - LOG("lx390_fdc_w, motor_w: %02x\n", d); - lx390_motor_w(d); - break; - case 7: - LOG("lx390_fdc_w, WD17xx data7, force: %02x\n", d); - m_wd1771->data_w(d ^ 0xff); - break; - } -}