summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/machine/ins8250.cpp43
-rw-r--r--src/devices/machine/ins8250.h7
-rw-r--r--src/mame/drivers/eispc.cpp118
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 <algorithm>
//#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<pc_lpt_device> m_lpt;
@@ -171,7 +178,10 @@ private:
required_device<eispc_keyboard_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 <int Channel>
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 )