diff options
Diffstat (limited to 'src/mame/heathzenith/mdt60.cpp')
-rw-r--r-- | src/mame/heathzenith/mdt60.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/mame/heathzenith/mdt60.cpp b/src/mame/heathzenith/mdt60.cpp new file mode 100644 index 00000000000..ea4a45d1558 --- /dev/null +++ b/src/mame/heathzenith/mdt60.cpp @@ -0,0 +1,269 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/************************************************************************** + + MDT 60 was designed by Zenith for use with Morrow's Micro-Decision + computers as a low-cost terminal with limited features. It appears + to be a stripped-down version of some previously designed terminal, + with the auxiliary port omitted from the Morrow version even though + the schematics published in its service manual include it. These + schematics also include the same keyboard used by the Z-29, though + the actual keyboard found with one unit was different. + +**************************************************************************/ + +#include "emu.h" +#include "bus/rs232/rs232.h" +#include "bus/z29_kbd/keyboard.h" +#include "cpu/m6502/m6502.h" +#include "machine/i8251.h" +#include "video/mc6845.h" +#include "screen.h" + + +namespace { + +class mdt60_state : public driver_device +{ +public: + mdt60_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_keyboard(*this, "keyboard") + , m_crtc(*this, "crtc") + , m_uart(*this, "uart%u", 0U) + , m_charram(*this, "charram") + , m_charrom(*this, "charrom") + , m_attrram(*this, "attrram") + , m_dip0(*this, "DIP0") + , m_baud_timer(nullptr) + , m_keyin(false) + , m_output_reg(0x3f) + , m_timer_output(false) + { + } + + void mdt60(machine_config &mconfig); + +protected: + virtual void machine_start() override ATTR_COLD; + virtual void machine_reset() override ATTR_COLD; + +private: + MC6845_UPDATE_ROW(update_row); + MC6845_ON_UPDATE_ADDR_CHANGED(update_cb); + TIMER_CALLBACK_MEMBER(baud_timer); + + void keyin_w(int state); + u8 dip0_r(offs_t offset); + void reg_w(u8 data); + + void mem_map(address_map &map) ATTR_COLD; + + required_device<cpu_device> m_maincpu; + required_device<z29_keyboard_port_device> m_keyboard; + required_device<r6545_1_device> m_crtc; + optional_device_array<i8251_device, 2> m_uart; + required_shared_ptr<u8> m_charram; + required_region_ptr<u8> m_charrom; + required_shared_ptr<u8> m_attrram; + required_ioport m_dip0; + + emu_timer *m_baud_timer; + + bool m_keyin; + u8 m_output_reg; + bool m_timer_output; +}; + +void mdt60_state::machine_start() +{ + m_baud_timer = timer_alloc(FUNC(mdt60_state::baud_timer), this); + m_baud_timer->adjust(attotime::zero); + + save_item(NAME(m_keyin)); + save_item(NAME(m_output_reg)); + save_item(NAME(m_timer_output)); +} + +void mdt60_state::machine_reset() +{ + reg_w(0); +} + + +TIMER_CALLBACK_MEMBER(mdt60_state::baud_timer) +{ + m_timer_output = !m_timer_output; + + // LS352 multiplexer has inverting outputs + m_uart[0]->write_rxc(!m_timer_output); + m_uart[0]->write_txc(!m_timer_output); + + constexpr auto time_base = 16.5888_MHz_XTAL / 9; + if ((m_output_reg & 0x06) == 0x00) + m_baud_timer->adjust(attotime::from_ticks(m_timer_output ? 2 : 4, time_base)); + else if ((m_output_reg & 0x06) == 0x06) + m_baud_timer->adjust(attotime::from_ticks(48, time_base)); + else + m_baud_timer->adjust(attotime::from_ticks(3 * (m_output_reg & 0x06), time_base)); +} + +MC6845_UPDATE_ROW(mdt60_state::update_row) +{ + u32 *pix = &bitmap.pix(y); + + for (unsigned x = 0; x < x_count; x++) + { + u8 cdata = m_charram[(ma + x) & 0x7ff]; + u8 adata = m_attrram[(ma + x) & 0x3ff] >> (BIT(ma + x, 10) ? 4 : 0); + u16 dots = m_charrom[cdata << 4 | ra] << 1 | BIT(adata, 3); + + if (x == cursor_x) + dots = ~dots; + if (BIT(adata, 2)) + dots = ~dots; + if (BIT(adata, 0) && (ra & 0xb) == 0xa) + dots = ~dots; + + rgb_t fg = BIT(adata, 1) ? rgb_t::white() : rgb_t(0xc0, 0xc0, 0xc0); + rgb_t bg = rgb_t::black(); + if (BIT(m_output_reg, 5)) + std::swap(fg, bg); + + for (int n = 8; n >= 0; n--) + *pix++ = BIT(dots, n) ? fg : bg; + } +} + +MC6845_ON_UPDATE_ADDR_CHANGED(mdt60_state::update_cb) +{ +} + + +void mdt60_state::keyin_w(int state) +{ + m_keyin = state; + m_maincpu->set_input_line(m6502_device::IRQ_LINE, state ? ASSERT_LINE : CLEAR_LINE); +} + +u8 mdt60_state::dip0_r(offs_t offset) +{ + u8 buffer = 0xff; + if (!BIT(offset, 0)) + buffer &= m_dip0->read() >> 1 | 0x80; + if (!BIT(offset, 1)) + buffer &= m_dip0->read() | 0xfe; + if (!m_keyin) + buffer &= 0x7f; + return buffer; +} + +void mdt60_state::reg_w(u8 data) +{ + if (BIT(data, 0) != BIT(m_output_reg, 0)) + m_keyboard->keyout_w(BIT(data, 0)); + + // Bits 1–2 & 3–4 select 1 of 4 baud rates for each UART; bit 5 selects inverse video + m_output_reg = data & 0x3f; +} + +void mdt60_state::mem_map(address_map &map) +{ + map(0x0000, 0x03ff).mirror(0x1800).ram(); + map(0x0400, 0x07ff).mirror(0x1800).ram().share("attrram"); + map(0x2000, 0x27ff).mirror(0x1800).ram().share("charram"); + map(0x4000, 0x4000).mirror(0x1ffe).rw(m_crtc, FUNC(r6545_1_device::status_r), FUNC(r6545_1_device::address_w)); + map(0x4001, 0x4001).mirror(0x1ffe).rw(m_crtc, FUNC(r6545_1_device::register_r), FUNC(r6545_1_device::register_w)); + map(0x6000, 0x6001).mirror(0x1ffe).rw(m_uart[0], FUNC(i8251_device::read), FUNC(i8251_device::write)); + //map(0x8000, 0x8001).mirror(0x1ffe).rw(m_uart[1], FUNC(i8251_device::read), FUNC(i8251_device::write)); + map(0xa000, 0xa003).mirror(0x1ffc).r(FUNC(mdt60_state::dip0_r)); + map(0xa000, 0xa000).mirror(0x1fff).w(FUNC(mdt60_state::reg_w)); + //map(0xc000, 0xc000).mirror(0x1fff).portr("DIP1"); + map(0xe000, 0xefff).mirror(0x1000).rom().region("coderom", 0); +} + + +static INPUT_PORTS_START(mdt60) + PORT_START("DIP0") + PORT_DIPNAME(0x03, 0x01, "Baud Rate") PORT_DIPLOCATION("SW1:1,2") + PORT_DIPSETTING(0x03, "300") + PORT_DIPSETTING(0x02, "1200") + PORT_DIPSETTING(0x01, "9600") + PORT_DIPSETTING(0x00, "19200") + PORT_DIPNAME(0x0c, 0x00, "Parity") PORT_DIPLOCATION("SW1:3,4") + PORT_DIPSETTING(0x04, "Odd") + PORT_DIPSETTING(0x0c, "Even") + PORT_DIPSETTING(0x00, "Mark") + PORT_DIPSETTING(0x08, "Space") + PORT_DIPNAME(0x70, 0x00, "Character Set") PORT_DIPLOCATION("SW1:5,6,7") + PORT_DIPSETTING(0x00, "U.S.") + PORT_DIPSETTING(0x10, "U.K.") + PORT_DIPSETTING(0x20, "French") + PORT_DIPSETTING(0x30, "German") + PORT_DIPSETTING(0x40, "Spanish") + PORT_DIPSETTING(0x50, "Danish/Norwegian") + PORT_DIPSETTING(0x60, "Swedish/Finnish") + PORT_DIPSETTING(0x70, "Italian") + PORT_DIPNAME(0x80, 0x80, "Function Key Sequence Type") PORT_DIPLOCATION("SW1:8") + PORT_DIPSETTING(0x80, "Morrow (FS)") + PORT_DIPSETTING(0x00, "TeleVideo (SOH...CR)") + + PORT_START("DIP1") + PORT_BIT(0xff, 0xff, IPT_UNUSED) // SW2 is unpopulated (as is LS244 at U20) +INPUT_PORTS_END + +// XTAL frequency is specified as 16.589 MHz on actual parts as well as in MDT 60 schematics. +// This has been assumed to be a lower-precision specification of the common 16.5888 MHz value. + +void mdt60_state::mdt60(machine_config &config) +{ + M6512(config, m_maincpu, 16.5888_MHz_XTAL / 9); // R6512AP + m_maincpu->set_addrmap(AS_PROGRAM, &mdt60_state::mem_map); + + I8251(config, m_uart[0], 16.5888_MHz_XTAL / 9); // TMP8251AP (U19) + m_uart[0]->rxrdy_handler().set_inputline(m_maincpu, m6512_device::NMI_LINE); + m_uart[0]->txd_handler().set("comm", FUNC(rs232_port_device::write_txd)); + m_uart[0]->dtr_handler().set("comm", FUNC(rs232_port_device::write_dtr)); + m_uart[0]->rts_handler().set("comm", FUNC(rs232_port_device::write_rts)); + + // UART 1 (at U28) is unpopulated and not used by code + + Z29_KEYBOARD(config, m_keyboard, z29_keyboards, "md"); + m_keyboard->keyin_callback().set(FUNC(mdt60_state::keyin_w)).invert(); + // Reset output (pin 2) is not connected + + screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); + screen.set_color(rgb_t::amber()); + screen.set_raw(16.5888_MHz_XTAL, 918, 0, 720, 301, 0, 288); + screen.set_screen_update(m_crtc, FUNC(r6545_1_device::screen_update)); + + R6545_1(config, m_crtc, 16.5888_MHz_XTAL / 9); // R6545-1AP + m_crtc->set_screen("screen"); + m_crtc->set_show_border_area(false); + m_crtc->set_char_width(9); + m_crtc->set_update_row_callback(FUNC(mdt60_state::update_row)); + m_crtc->set_on_update_addr_change_callback(FUNC(mdt60_state::update_cb)); + // LPEN is tied to GND (code uses this to determine CRTC type) + + rs232_port_device &comm(RS232_PORT(config, "comm", default_rs232_devices, "loopback")); + // Service Manual suggests shorting pins with a bent paper clip if no DB-25 loopback connector is available ;-) + comm.rxd_handler().set(m_uart[0], FUNC(i8251_device::write_rxd)); + comm.dsr_handler().set(m_uart[0], FUNC(i8251_device::write_dsr)); + comm.cts_handler().set(m_uart[0], FUNC(i8251_device::write_cts)); +} + + +ROM_START(mdt60) + ROM_REGION(0x1000, "coderom", 0) + // "ROM REV. 1.7 -- Copyright (C) 1983 by Morrow Designs, Inc." + ROM_LOAD("vb-rom_1.7_bcfd.u9", 0x0000, 0x1000, CRC(3902f7b3) SHA1(ee0ce7f68efe20efe1ab8a44888e276f08fe2d07)) + + ROM_REGION(0x1000, "charrom", 0) + ROM_LOAD("char_gen_b207.u6", 0x0000, 0x1000, CRC(b19432da) SHA1(fa73641c08b778f19a17676a7f4074f69b7b55dd)) +ROM_END + +} // anonymous namespace + + +SYST(1983, mdt60, 0, 0, mdt60, mdt60, mdt60_state, empty_init, "Morrow Designs", "MDT 60 Video Display Terminal", MACHINE_SUPPORTS_SAVE) |