summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/facit4440.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/facit4440.cpp')
-rw-r--r--src/mame/drivers/facit4440.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/mame/drivers/facit4440.cpp b/src/mame/drivers/facit4440.cpp
new file mode 100644
index 00000000000..c5cb37d6819
--- /dev/null
+++ b/src/mame/drivers/facit4440.cpp
@@ -0,0 +1,265 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/****************************************************************************
+
+ Skeleton driver for Facit 4440 Twist terminal.
+
+ The “Twist” sobriquet refers to the adjustable orientation of the
+ monitor, whose normally black-on-white display has the dimensions of a
+ sheet of A4 paper. It displays 24 lines of 80 characters in landscape
+ mode and 72 lines of rather smaller characters in portrait mode.
+
+ No user manual for this terminal has been found. The layout of the
+ keyboard is also unclear, not to mention the serial protocol.
+
+ Nonvolatile settings are configured through VT100-style setup screens.
+ Several non-ANSI emulation modes are also available.
+
+ An OEM customized version was sold by Norsk Data AS as the ND 319 Twist.
+
+ The timing of the display circuit seems a bit uncertain. Promotional
+ information claims that it has a 40 MHz bandwidth, but this may be only
+ a nominal maximum rating since the only high-frequency oscillator seen
+ on the PCB is 32 MHz. It is unclear how the CRTC parameters and this
+ value can produce the documented 65 Hz refresh rate. The actual circuit
+ also generates alarming levels of electromagnetic radiation.
+
+****************************************************************************/
+
+#include "emu.h"
+//#include "bus/rs232/rs232.h"
+#include "cpu/z80/z80.h"
+#include "machine/clock.h"
+#include "machine/er1400.h"
+#include "machine/z80ctc.h"
+#include "machine/z80dart.h"
+#include "video/mc6845.h"
+#include "screen.h"
+
+class facit4440_state : public driver_device
+{
+public:
+ facit4440_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_earom(*this, "earom%u", 0U)
+ , m_crtc(*this, "crtc")
+ , m_test(*this, "TEST")
+ , m_chargen(*this, "chargen")
+ , m_vidprom(*this, "vidprom")
+ {
+ }
+
+ void facit4440(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+
+private:
+ void earom_latch_w(u8 data);
+ void control_2000_w(u8 data);
+ u8 misc_status_r();
+ void control_6000_w(u8 data);
+ void control_a000_w(u8 data);
+
+ DECLARE_WRITE_LINE_MEMBER(vsync_w);
+
+ MC6845_UPDATE_ROW(update_row);
+
+ void mem_map(address_map &map);
+ void io_map(address_map &map);
+
+ required_device<z80_device> m_maincpu;
+ required_device_array<er1400_device, 2> m_earom;
+ required_device<mc6845_device> m_crtc;
+
+ required_ioport m_test;
+
+ required_region_ptr<u8> m_chargen;
+ required_region_ptr<u8> m_vidprom;
+
+ u8 m_control_latch[3];
+};
+
+void facit4440_state::earom_latch_w(u8 data)
+{
+ // SN74LS174 latch + SN7406 inverter
+
+ // Prevent outputs from interfering with data reads
+ if (!BIT(data, 2))
+ data &= 0xfc;
+
+ // FIXME: clock must be written first here due to data/control setup time
+ m_earom[0]->clock_w(BIT(data, 5));
+ m_earom[1]->clock_w(BIT(data, 5));
+
+ m_earom[0]->data_w(BIT(data, 1));
+ m_earom[1]->data_w(BIT(data, 0));
+
+ for (auto &earom : m_earom)
+ {
+ earom->c2_w(BIT(data, 2));
+ earom->c1_w(BIT(data, 3));
+ earom->c3_w(BIT(data, 4));
+ }
+}
+
+void facit4440_state::control_2000_w(u8 data)
+{
+ m_control_latch[0] = data;
+
+ if (!BIT(m_control_latch[0], 5))
+ m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+}
+
+void facit4440_state::control_6000_w(u8 data)
+{
+ m_control_latch[1] = data;
+}
+
+void facit4440_state::control_a000_w(u8 data)
+{
+ m_control_latch[2] = data;
+}
+
+u8 facit4440_state::misc_status_r()
+{
+ u8 status = m_test->read() << 6;
+
+ status |= m_earom[0]->data_r() << 4;
+ status |= m_earom[1]->data_r() << 3;
+
+ status |= m_crtc->vsync_r() << 1;
+ // TODO: what should bit 0 be?
+
+ return status;
+}
+
+WRITE_LINE_MEMBER(facit4440_state::vsync_w)
+{
+ if (BIT(m_control_latch[0], 5))
+ m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
+}
+
+MC6845_UPDATE_ROW(facit4440_state::update_row)
+{
+}
+
+void facit4440_state::mem_map(address_map &map)
+{
+ map(0x0000, 0x7fff).rom().region("mainprg", 0);
+ map(0x0000, 0x0000).w(FUNC(facit4440_state::earom_latch_w));
+ map(0x2000, 0x2000).w(FUNC(facit4440_state::control_2000_w));
+ map(0x4000, 0x4000).mirror(0x1ffe).w("crtc", FUNC(mc6845_device::address_w));
+ map(0x4001, 0x4001).mirror(0x1ffe).w("crtc", FUNC(mc6845_device::register_w));
+ map(0x6000, 0x6000).w(FUNC(facit4440_state::control_6000_w));
+ map(0x8000, 0x8000).r(FUNC(facit4440_state::misc_status_r));
+ map(0x8000, 0x8050).nopw();
+ map(0xa000, 0xbfff).rom().region("testprg", 0);
+ map(0xa000, 0xa000).w(FUNC(facit4440_state::control_a000_w));
+ map(0xc000, 0xdfff).ram(); // 4x TMM2016AP-90
+ map(0xe000, 0xffff).ram(); // 4x TMM2016AP-90
+}
+
+void facit4440_state::io_map(address_map &map)
+{
+ map.global_mask(0xff);
+ map(0xec, 0xef).rw("dart2", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
+ map(0xf4, 0xf7).rw("dart1", FUNC(z80dart_device::cd_ba_r), FUNC(z80dart_device::cd_ba_w));
+ map(0xf8, 0xfb).rw("ctc", FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
+}
+
+void facit4440_state::machine_start()
+{
+ m_control_latch[0] = m_control_latch[1] = m_control_latch[2] = 0;
+
+ save_item(NAME(m_control_latch));
+}
+
+static INPUT_PORTS_START(facit4440)
+ PORT_START("TEST")
+ PORT_CONFNAME(3, 0, "Test Mode")
+ PORT_CONFSETTING(0, DEF_STR(Off))
+ PORT_CONFSETTING(3, DEF_STR(On))
+ PORT_CONFSETTING(2, "Burn In")
+INPUT_PORTS_END
+
+static const z80_daisy_config daisy_chain[] =
+{
+ { "ctc" },
+ { "dart1" },
+ { "dart2" },
+ { nullptr }
+};
+
+void facit4440_state::facit4440(machine_config &config)
+{
+ constexpr u32 CHAR_WIDTH = 8;
+ constexpr u32 FAKE_DOT_CLOCK = 65 * 103 * 621 * CHAR_WIDTH;
+
+ Z80(config, m_maincpu, 32_MHz_XTAL / 8); // clock unknown
+ m_maincpu->set_addrmap(AS_PROGRAM, &facit4440_state::mem_map);
+ m_maincpu->set_addrmap(AS_IO, &facit4440_state::io_map);
+ m_maincpu->set_daisy_config(daisy_chain);
+
+ ER1400(config, m_earom[0]); // M5G1400P
+ ER1400(config, m_earom[1]); // M5G1400P
+
+ z80ctc_device &ctc(Z80CTC(config, "ctc", 32_MHz_XTAL / 8));
+ ctc.intr_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
+ ctc.zc_callback<0>().set("dart1", FUNC(z80dart_device::txca_w));
+ ctc.zc_callback<1>().set("dart1", FUNC(z80dart_device::rxca_w));
+ ctc.zc_callback<2>().set("dart1", FUNC(z80dart_device::txcb_w));
+ ctc.zc_callback<2>().append("dart1", FUNC(z80dart_device::rxcb_w));
+
+ clock_device &baudclk(CLOCK(config, "baudclk", 2.4576_MHz_XTAL / 4));
+ baudclk.signal_handler().set("ctc", FUNC(z80ctc_device::trg0));
+ baudclk.signal_handler().append("ctc", FUNC(z80ctc_device::trg1));
+ baudclk.signal_handler().append("ctc", FUNC(z80ctc_device::trg2));
+
+ clock_device &keybclk(CLOCK(config, "keybclk", 2.4576_MHz_XTAL / 32)); // unclear
+ keybclk.signal_handler().set("dart2", FUNC(z80dart_device::txca_w));
+ keybclk.signal_handler().append("dart2", FUNC(z80dart_device::rxca_w));
+
+ z80dart_device &dart1(Z80DART(config, "dart1", 32_MHz_XTAL / 8)); // Z80ADART
+ dart1.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
+ dart1.out_txda_callback().set("dart1", FUNC(z80dart_device::rxa_w)); // temporary loopback for testing
+ dart1.out_rtsa_callback().set("dart1", FUNC(z80dart_device::ctsa_w)); // temporary loopback for testing
+ dart1.out_dtra_callback().set("dart1", FUNC(z80dart_device::dcdb_w)); // temporary loopback for testing
+ dart1.out_txdb_callback().set("dart1", FUNC(z80dart_device::rxb_w)); // temporary loopback for testing
+
+ z80dart_device &dart2(Z80DART(config, "dart2", 32_MHz_XTAL / 8)); // Z80ADART
+ dart2.out_int_callback().set_inputline(m_maincpu, INPUT_LINE_IRQ0);
+ dart2.out_txda_callback().set("dart2", FUNC(z80dart_device::rxa_w)); // FIXME: serial keyboard needed here
+ // Channel B is not used
+
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
+ screen.set_raw(FAKE_DOT_CLOCK, 103 * CHAR_WIDTH, 0, 80 * CHAR_WIDTH, 621, 0, 592);
+ screen.set_screen_update("crtc", FUNC(mc6845_device::screen_update));
+
+ MC6845(config, m_crtc, FAKE_DOT_CLOCK / CHAR_WIDTH); // HD46505SP-2
+ m_crtc->set_char_width(CHAR_WIDTH);
+ m_crtc->set_update_row_callback(FUNC(facit4440_state::update_row), this);
+ m_crtc->out_hsync_callback().set("ctc", FUNC(z80ctc_device::trg3));
+ m_crtc->out_vsync_callback().set(FUNC(facit4440_state::vsync_w));
+}
+
+
+ROM_START(facit4440)
+ ROM_REGION(0x8000, "mainprg", 0)
+ ROM_LOAD("rom7.bin", 0x0000, 0x4000, CRC(a8da2b11) SHA1(4436ef14c29ae299f7bc338748158771c02d02a9))
+ ROM_LOAD("rom6.bin", 0x4000, 0x4000, CRC(790b7642) SHA1(688a80cbf011e5c14f501e11fe0e3bf64a85bbd7))
+
+ ROM_REGION(0x2000, "testprg", 0)
+ ROM_LOAD("rom5.bin", 0x0000, 0x2000, CRC(715d02b6) SHA1(e304718dbdc8867ac01909fd2d027e5014a8c4f9))
+
+ ROM_REGION(0x3000, "chargen", 0) // order unknown
+ ROM_LOAD("rom1.bin", 0x0000, 0x1000, CRC(b503c173) SHA1(209bf59e2e9953179d04c4e768fc41574e039d36))
+ ROM_LOAD("rom3.bin", 0x1000, 0x1000, CRC(a55a25d9) SHA1(c0d321e65f214adee01bf5f8c495b2518fa31b7b))
+ ROM_LOAD("rom4.bin", 0x2000, 0x1000, CRC(52004ef8) SHA1(50d6e2eb48f60db3a3c9d206fc40d3294b6adc0e))
+
+ ROM_REGION(0x0800, "vidprom", 0)
+ ROM_LOAD("rom2.bin", 0x0000, 0x0800, CRC(9e1a190c) SHA1(fb08ee806f1056bcdfb5b08ea85995e1d3d01298))
+ROM_END
+
+COMP(1984, facit4440, 0, 0, facit4440, facit4440, facit4440_state, empty_init, "Facit", "4440 Twist (30M-F1)", MACHINE_IS_SKELETON)