From e94984babf59e3c8c30465227d4b7d7fc2478421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Larsson=20Edstr=C3=B6m?= Date: Fri, 15 Nov 2019 10:41:46 +0100 Subject: ins8250 support for BAUDOUT pin (#5858) and * ins8250: improved support for BAUDOUT pin and added rclk_w to allow special receiver clock circuit required for descrete BRG of the epc driver * epc: Added 8087 support * epc: Added S21 jumper supporting secondary keyboard connector interface and completed the descrete BRG --- src/devices/machine/ins8250.cpp | 43 +++++++++++++-- src/devices/machine/ins8250.h | 7 +++ src/mame/drivers/eispc.cpp | 118 +++++++++++++++++++++++++++++----------- 3 files changed, 130 insertions(+), 38 deletions(-) diff --git a/src/devices/machine/ins8250.cpp b/src/devices/machine/ins8250.cpp index ec5d8e041d7..25c80d0147d 100644 --- a/src/devices/machine/ins8250.cpp +++ b/src/devices/machine/ins8250.cpp @@ -100,6 +100,8 @@ History: #include //#define VERBOSE 1 +//#define LOG_OUTPUT_STREAM std::cout + #include "logmacro.h" DEFINE_DEVICE_TYPE(INS8250, ins8250_device, "ins8250", "National Semiconductor INS8250 UART") @@ -117,6 +119,9 @@ ins8250_uart_device::ins8250_uart_device(const machine_config &mconfig, device_t , m_out_int_cb(*this) , m_out_out1_cb(*this) , m_out_out2_cb(*this) + , m_out_baudout_cb(*this) + , m_brg_clock(true) + , m_txd(1) , m_rxd(1) , m_dcd(1) , m_dsr(1) @@ -245,16 +250,33 @@ READ_LINE_MEMBER(ins8250_uart_device::intrpt_r) return !BIT(m_regs.iir, 0); } +TIMER_CALLBACK_MEMBER(ins8250_uart_device::brg_clock) +{ + if (!is_transmit_register_empty()) + { + tx_clock_w(m_brg_clock); + } + if (is_receive_register_synchronized()) + { + if (m_out_baudout_cb.isnull()) + { + device_serial_interface::rx_clock_w(m_brg_clock); + } + else + { + m_out_baudout_cb(m_brg_clock); + } + } + m_brg_clock = !m_brg_clock; + m_brg->adjust(((clock() && (m_regs.dl) ? (attotime::from_hz(clock()) * m_regs.dl * 16) : attotime::never) / 2)); +} + // Baud rate generator is reset after writing to either byte of divisor latch void ins8250_uart_device::update_baud_rate() { LOG("%.1f baud selected (divisor = %d)\n", double(clock()) / (m_regs.dl * 16), m_regs.dl); - set_rate(clock(), m_regs.dl * 16); - // FIXME: Baud rate generator should not affect transmitter or receiver, but device_serial_interface resets them regardless. - // If the transmitter is still running at this time and we don't flush it, the shift register will never be emptied! - if (!(m_regs.lsr & INS8250_LSR_TSRE)) - tra_complete(); + m_brg->adjust(((clock() && (m_regs.dl) ? (attotime::from_hz(clock()) * m_regs.dl * 16) : attotime::never) / 2)); } void ins8250_uart_device::ins8250_w(offs_t offset, u8 data) @@ -604,6 +626,11 @@ void ins8250_uart_device::update_msr() trigger_int(COM_INT_PENDING_MODEM_STATUS_REGISTER); } +WRITE_LINE_MEMBER(ins8250_uart_device::rclk_w) +{ + device_serial_interface::rx_clock_w(state); +} + WRITE_LINE_MEMBER(ins8250_uart_device::dcd_w) { m_dcd = state; @@ -644,9 +671,10 @@ void ins8250_uart_device::device_start() m_out_int_cb.resolve_safe(); m_out_out1_cb.resolve_safe(); m_out_out2_cb.resolve_safe(); + m_out_baudout_cb.resolve_safe(); set_tra_rate(0); set_rcv_rate(0); - + m_brg = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ins8250_uart_device::brg_clock), this)); save_item(NAME(m_regs.thr)); save_item(NAME(m_regs.rbr)); save_item(NAME(m_regs.ier)); @@ -659,6 +687,7 @@ void ins8250_uart_device::device_start() save_item(NAME(m_regs.msr)); save_item(NAME(m_regs.scr)); save_item(NAME(m_int_pending)); + save_item(NAME(m_brg_clock)); save_item(NAME(m_txd)); save_item(NAME(m_rxd)); save_item(NAME(m_dcd)); @@ -680,12 +709,14 @@ void ins8250_uart_device::device_reset() update_interrupt(); receive_register_reset(); transmit_register_reset(); + m_brg_clock = true; m_txd = 1; m_out_tx_cb(1); m_out_rts_cb(1); m_out_dtr_cb(1); m_out_out1_cb(1); m_out_out2_cb(1); + m_out_baudout_cb(1); } void ns16550_device::device_start() diff --git a/src/devices/machine/ins8250.h b/src/devices/machine/ins8250.h index 5f9a6bd1793..2ad59e05389 100644 --- a/src/devices/machine/ins8250.h +++ b/src/devices/machine/ins8250.h @@ -26,10 +26,12 @@ public: auto out_int_callback() { return m_out_int_cb.bind(); } auto out_out1_callback() { return m_out_out1_cb.bind(); } auto out_out2_callback() { return m_out_out2_cb.bind(); } + auto out_baudout_callback() { return m_out_baudout_cb.bind(); } void ins8250_w(offs_t offset, u8 data); u8 ins8250_r(offs_t offset); + DECLARE_WRITE_LINE_MEMBER(rclk_w); DECLARE_WRITE_LINE_MEMBER(dcd_w); DECLARE_WRITE_LINE_MEMBER(dsr_w); DECLARE_WRITE_LINE_MEMBER(ri_w); @@ -86,6 +88,11 @@ private: devcb_write_line m_out_int_cb; devcb_write_line m_out_out1_cb; devcb_write_line m_out_out2_cb; + devcb_write_line m_out_baudout_cb; + + emu_timer *m_brg; + TIMER_CALLBACK_MEMBER(brg_clock); + bool m_brg_clock; void update_interrupt(); void update_msr(); diff --git a/src/mame/drivers/eispc.cpp b/src/mame/drivers/eispc.cpp index 4536ab11ddf..f009b9ecf32 100644 --- a/src/mame/drivers/eispc.cpp +++ b/src/mame/drivers/eispc.cpp @@ -26,8 +26,8 @@ * TODO * - Complete the Ericsson 1070 MDA ISA board and test all the graphics modes including 640x400 (aka HR) * - Add the Ericsson 1065 HDC and boot from a hard drive - * - Implement the descrete baudrate generator - * - Implement logic around enabling/resetting NMI and ISA bus I/O CHK + * - Add softlist + * - Pass the diagnostics software at EPC5.IMD * * CREDITS The driver code is inspired from m24.cpp, myb3k.cpp and genpc.cpp. Information about the EPC has * been contributed by many, mainly the people at Dalby Computer museum http://www.datormuseum.se/ @@ -48,6 +48,7 @@ // Devices #include "cpu/i86/i86.h" #include "machine/am9517a.h" +#include "machine/i8087.h" #include "machine/i8251.h" #include "machine/i8255.h" #include "machine/pit8253.h" @@ -82,8 +83,10 @@ #define LOG_LPT (1U << 8) #define LOG_NMI (1U << 9) #define LOG_BITS (1U << 10) +#define LOG_FPU (1U << 11) +#define LOG_COM (1U << 12) -//#define VERBOSE (LOG_BITS|LOG_KBD|LOG_IRQ) +//#define VERBOSE (LOG_COM) //#define LOG_OUTPUT_STREAM std::cout #include "logmacro.h" @@ -97,7 +100,9 @@ #define LOGFDC(...) LOGMASKED(LOG_FDC, __VA_ARGS__) #define LOGLPT(...) LOGMASKED(LOG_LPT, __VA_ARGS__) #define LOGNMI(...) LOGMASKED(LOG_NMI, __VA_ARGS__) -#define LOGBITS(...) LOGMASKED(LOG_BITS, __VA_ARGS__) +#define LOGBITS(...) LOGMASKED(LOG_BITS, __VA_ARGS__) +#define LOGFPU(...) LOGMASKED(LOG_FPU, __VA_ARGS__) +#define LOGCOM(...) LOGMASKED(LOG_COM, __VA_ARGS__) class epc_state : public driver_device { @@ -111,6 +116,7 @@ public: , m_ppi8255(*this, "ppi8255") , m_io_dsw(*this, "DSW") , m_io_j10(*this, "J10") + , m_io_s21(*this, "S21") , m_lpt(*this, "lpt") , m_kbd8251(*this, "kbd8251") , m_keyboard(*this, "keyboard") @@ -120,7 +126,7 @@ public: , m_speaker(*this, "speaker") , m_fdc(*this, "fdc") , m_floppy_connectors(*this, "fdc:%u", 0) - , m_uart(*this, "uart8250") + , m_uart(*this, "uart") { } void epc(machine_config &config); @@ -162,6 +168,7 @@ private: uint8_t m_ppi_portb; required_ioport m_io_dsw; required_ioport m_io_j10; + required_ioport m_io_s21; // Printer port optional_device m_lpt; @@ -171,7 +178,10 @@ private: required_device m_keyboard; emu_timer *m_kbdclk_timer; TIMER_CALLBACK_MEMBER(rxtxclk_w); - int m_rxtx_clk_state; + bool m_8251rxtx_clk_state; + bool m_kbdclk_state; + bool m_8251dtr_state; + int m_kbdclk; output_finder<3> m_leds; // Interrupt Controller @@ -466,7 +476,10 @@ void epc_state::machine_start() save_item(NAME(m_int)); save_item(NAME(m_dreq0_ck)); save_item(NAME(m_ppi_portb)); - save_item(NAME(m_rxtx_clk_state)); + save_item(NAME(m_8251rxtx_clk_state)); + save_item(NAME(m_kbdclk_state)); + save_item(NAME(m_kbdclk)); + save_item(NAME(m_8251dtr_state)); save_item(NAME(m_nmi_enabled)); save_item(NAME(m_8087_int)); save_item(NAME(m_parer_int)); @@ -491,7 +504,10 @@ void epc_state::machine_reset() m_int = 1; m_dreq0_ck = true; m_ppi_portb = 0; - m_rxtx_clk_state = 0; + m_8251rxtx_clk_state = 0; + m_kbdclk_state = 0; + m_kbdclk = 0; + m_8251dtr_state = 1; m_nmi_enabled = 0; m_8087_int = 0; m_parer_int = 0; @@ -510,19 +526,34 @@ void epc_state::machine_reset() void epc_state::init_epc() { - /* Keyboard UART Rxc/Txc is 19.2 kHz from x96 divider */ + /* Keyboard UART Rxc/Txc is 19.2 kHz from x960 divider */ m_kbdclk_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(epc_state::rxtxclk_w), this)); - m_kbdclk_timer->adjust(attotime::from_hz((XTAL(18'432'000) / 96) / 5)); + m_kbdclk_timer->adjust(attotime::from_hz(XTAL(18'432'000) / 960) / 2); } TIMER_CALLBACK_MEMBER(epc_state::rxtxclk_w) { - m_kbd8251->write_rxc(m_rxtx_clk_state); - m_kbd8251->write_txc(m_rxtx_clk_state); - m_rxtx_clk_state ^= 0x01; - m_kbdclk_timer->adjust(attotime::from_hz((XTAL(18'432'000) / 96) / 5)); -} + m_kbd8251->write_rxc(m_8251rxtx_clk_state); + m_kbd8251->write_txc(m_8251rxtx_clk_state); + + if (!m_8251dtr_state) m_uart->rclk_w(m_8251rxtx_clk_state); + + m_8251rxtx_clk_state = !m_8251rxtx_clk_state; + + // If CLK signal is jumpered in instead of reset signal for the keyboard + if ((m_io_s21->read() & 0x01) == 0x01) + { + if (m_kbdclk++ >= 4) // Frequncy is taken out of the same divider as the rxtx clock but 2 steps later + { + m_keyboard->rst_line_w(m_kbdclk_state); + m_kbdclk = 0; + m_kbdclk_state = !m_kbdclk_state; + } + } + /* Keyboard UART Rxc/Txc is 19.2 kHz from x960 divider ( 15 (74ls161) * 4 (74ls393.1) * 16 (74ls393) ) */ + m_kbdclk_timer->adjust(attotime::from_hz(XTAL(18'432'000) / 960) / 2); +} template uint8_t epc_state::epc_dma8237_io_r(offs_t offset) @@ -692,15 +723,18 @@ WRITE8_MEMBER( epc_state::ppi_portb_w ) if (changed & 0x40) { - if (m_ppi_portb & 0x40) + if ((m_io_s21->read() & 0x01) == 0x00) { - LOGKBD("PB6 set, clearing Keyboard RESET\n"); - m_keyboard->rst_line_w(CLEAR_LINE); - } - else - { - LOGKBD("PB6 cleared, asserting Keyboard RESET\n"); - m_keyboard->rst_line_w(ASSERT_LINE); + if (m_ppi_portb & 0x40) + { + LOGKBD("PB6 set, clearing Keyboard RESET\n"); + m_keyboard->rst_line_w(CLEAR_LINE); + } + else + { + LOGKBD("PB6 cleared, asserting Keyboard RESET\n"); + m_keyboard->rst_line_w(ASSERT_LINE); + } } } @@ -745,10 +779,23 @@ void epc_state::epc(machine_config &config) { config.set_default_layout(layout_epc); + // CPU I8088(config, m_maincpu, XTAL(14'318'181) / 3.0); // TWE crystal marked X1 verified divided through a 82874 m_maincpu->set_addrmap(AS_PROGRAM, &epc_state::epc_map); m_maincpu->set_addrmap(AS_IO, &epc_state::epc_io); m_maincpu->set_irq_acknowledge_callback("pic8259", FUNC(pic8259_device::inta_cb)); + m_maincpu->esc_opcode_handler().set("fpu8087", FUNC(i8087_device::insn_w)); + m_maincpu->esc_data_handler().set("fpu8087", FUNC(i8087_device::addr_w)); + + i8087_device &i8087(I8087(config, "fpu8087", XTAL(14'318'181) / 3.0)); + i8087.set_space_88(m_maincpu, AS_PROGRAM); + i8087.irq().set([this](bool state) + { + LOGFPU("8087 INT: %d\n", state); + m_8087_int = state; + update_nmi(); + }); + i8087.busy().set_inputline(m_maincpu, INPUT_LINE_TEST); // DMA AM9517A(config, m_dma8237a, XTAL(14'318'181) / 3.0); // TWE crystal marked X1 verified @@ -805,7 +852,8 @@ void epc_state::epc(machine_config &config) }); m_kbd8251->dtr_handler().set([this](bool state) // Controls RCLK for INS8250, either 19.2KHz or INS8250 BAUDOUT { - LOGKBD("KBD DTR: %d\n", state ? 1 : 0); // TODO: Implement clock selection mux, need to check what state does what + LOGCOM("KBD DTR: %d\n", state ? 1 : 0); // TODO: Implement clock selection mux, need to check what state does what + m_8251dtr_state = state; }); // Interrupt Controller @@ -888,18 +936,19 @@ void epc_state::epc(machine_config &config) // system board UART TODO: Implement the descrete "Baud Rate Clock" from schematics that generates clocks for the 8250 INS8250(config, m_uart, XTAL(18'432'000) / 10); // TEW crystal marked X2 verified. TODO: Let 8051 DTR control RCLK (see above) - m_uart->out_tx_callback().set("uart", FUNC(rs232_port_device::write_txd)); - m_uart->out_dtr_callback().set("uart", FUNC(rs232_port_device::write_dtr)); - m_uart->out_rts_callback().set("uart", FUNC(rs232_port_device::write_rts)); + m_uart->out_tx_callback().set("com1", FUNC(rs232_port_device::write_txd)); + m_uart->out_dtr_callback().set("com1", FUNC(rs232_port_device::write_dtr)); + m_uart->out_rts_callback().set("com1", FUNC(rs232_port_device::write_rts)); m_uart->out_int_callback().set([this](int state) { // Jumper field J10 decides what IRQ to pull - if ((m_io_j10->read() & 0x03) == 0x02) { LOGIRQ("UART IRQ2: %d\n", state); m_pic8259->ir2_w(state); } - if ((m_io_j10->read() & 0x0c) == 0x08) { LOGIRQ("UART IRQ3: %d\n", state); m_pic8259->ir3_w(state); } - if ((m_io_j10->read() & 0x30) == 0x20) { LOGIRQ("UART IRQ4: %d\n", state); m_pic8259->ir4_w(state); } // Factory setting - if ((m_io_j10->read() & 0xc0) == 0x80) { LOGIRQ("UART IRQ7: %d\n", state); m_pic8259->ir7_w(state); } + if ((m_io_j10->read() & 0x03) == 0x02) { LOGCOM("UART IRQ2: %d\n", state); m_pic8259->ir2_w(state); } + if ((m_io_j10->read() & 0x0c) == 0x08) { LOGCOM("UART IRQ3: %d\n", state); m_pic8259->ir3_w(state); } + if ((m_io_j10->read() & 0x30) == 0x20) { LOGCOM("UART IRQ4: %d\n", state); m_pic8259->ir4_w(state); } // Factory setting + if ((m_io_j10->read() & 0xc0) == 0x80) { LOGCOM("UART IRQ7: %d\n", state); m_pic8259->ir7_w(state); } }); + m_uart->out_baudout_callback().set([this](int state){ if (m_8251dtr_state) m_uart->rclk_w(state); }); - rs232_port_device &rs232(RS232_PORT(config, "uart", default_rs232_devices, nullptr)); + rs232_port_device &rs232(RS232_PORT(config, "com1", default_rs232_devices, nullptr)); rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w)); rs232.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w)); rs232.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w)); @@ -1002,6 +1051,11 @@ static INPUT_PORTS_START( epc_ports ) PORT_DIPSETTING(0x00, "no jumper") PORT_DIPSETTING(0x40, "LPT") PORT_DIPSETTING(0x80, "COM") + + PORT_START("S21") // Jumper 0=PB6 reset, 1=KBCLK 4.8kHz - what to send to keyboard pin 3 + PORT_DIPNAME(0x01, 0x00, "Keyboard Clock/Reset pin") + PORT_DIPSETTING(0x00, "PB6") + PORT_DIPSETTING(0x01, "4.8kHz") // This setting is apparantly for another keyboard, currently unknown INPUT_PORTS_END ROM_START( epc ) -- cgit v1.2.3