From 92d7227ec0cde61a7283d8b303035e10ca502bab Mon Sep 17 00:00:00 2001 From: 68bit Date: Sun, 6 Oct 2019 20:27:09 +1100 Subject: [PATCH] MEK6800D1: early Motorola 6800 design evaluation board The MIKBUG monitor is working. The terminal support for reading and 'punching' tapes is not yet implemented. --- src/mame/drivers/mekd1.cpp | 475 +++++++++++++++++++++++++++++++++++++ src/mame/mame.lst | 3 + src/mame/mess.flt | 1 + 3 files changed, 479 insertions(+) create mode 100644 src/mame/drivers/mekd1.cpp diff --git a/src/mame/drivers/mekd1.cpp b/src/mame/drivers/mekd1.cpp new file mode 100644 index 00000000000..d2fc5ad6f8d --- /dev/null +++ b/src/mame/drivers/mekd1.cpp @@ -0,0 +1,475 @@ +// license:BSD-3-Clause +// copyright-holders:68bit +/*************************************************************************** + +Motorola Evaluation Kit 6800 D1 - MEK6800D1 + +The monitor is supplied in the MCM6830L7 ROM and named MIKBUG rev 9. MIKBUG is +a 512 byte monitor. The MIKBUG commands are: + +M aaaa - memory examine and change at address 'aaaa'. + hh - modify content with hex value 'hh' and increase address. If the + memory can not be changed then it prints '?' and exits the + memory examine. + and any other key exits the memory examine. The documentation + suggests using a carriage return. + Any other key increases the address. + +R - Register display: CC B A X PC SP. The registers can be modified at: + 0xa043 CC + 0xa044 B + 0xa045 A + 0xa046 X high + 0xa047 X low + 0xa048 PC high + 0xa049 PC low + 0xa008 SP high + 0xa009 SP low + +L - Loads data, in S19 format, as generated by the assembler. If there is a + checksum failure then it prints '?' and exits the load. An 'S9' record + exits the load. Note that the MIKBUG punch command 'P' does not write a + 'S9' record so it is necessary to manually enter a 'S9' sequence at the + end of the load when loading a MIKBUG generated tape! + +P - Print or Punch memory dump. The start address is loaded from 0xa002 to + 0xa003, and the end address is loaded from 0xa004 to 0xa005. The output is + in S19 format and can be loaded with the 'L' command, but the end-of-file + 'S9' record is not emitted. + +G - Go to user code. The registers are loaded from the addresses noted above + 0xa043 to 0xa049 and 0xa008 to 0xa009 for the stack pointer. Breakpoints + can be manually set by inserting a SWI (0x3f) instruction, and when these + are encountered control returns to MIKBUG and the registers are save and + printed. + +An IRQ handler can be defined at 0xa000 to 0xa001. +An NMI handler can be defined at 0xa006 to 0xa007. + +MIKBUG was designed to work with the ASR33 Teletypewriter and drives the +'reader control' line high during a 'load' operation intended for a reader +relay control which was a common modification. There is no signal line driven +for a 'punch' operation. TODO might be able to at least use this 'reader +control' line to switch to input from a tape. + +MIKBUG was designed to work with the Texas Instruments 733 ASR twin tape +cassette terminal with automatic device control and issues the following +control codes when loading and 'punching' a tape. TODO perhaps these could +be detected and serial I/O diverted to a cassette. + Code 0x11 DC1 - Tape playback on + Code 0x12 DC2 - Tape record on. + Code 0x13 DC4 - Tape playback off. + Code 0x14 DC4 - Tape record off. + +The serial bit rate is controlled by a MC14536 timer and is variable. Common +rates are 110 baud and 300 baud, for the ASR33 and 733 ASR terminals +respectively, but is was practical to run at higher rates up to about 4800 +baud. + +Memory is expandable off-board with some modifications, to at least 8K. There +were versions of the resident editor and assembler for use with MIKBUG, that +required the expanded RAM. + +The board hosts a MC6850 ACIA but this is not used by MIKBUG so it is a user +ACIA. The clock is supplied off board and the documentation offers a design +suggestion using the MC14411 and this is emulated here as documented. + +TODO could add a reset button, and an NMI button, and an LED for the reader +relay line. Might want to default the terminal to echo characters. + +References: +1. Assembly Instructions for the Motorola M6800 Design Evaluation Kit, 1975. +2. Engineering Note 100, MCM6830L7 MIKBUG/MINIBUG ROM. + +****************************************************************************/ + +#include "emu.h" +#include "cpu/m6800/m6800.h" +#include "machine/6821pia.h" +#include "machine/6850acia.h" +#include "machine/mc14411.h" +#include "machine/input_merger.h" +#include "machine/timer.h" +#include "bus/rs232/rs232.h" +#include "machine/terminal.h" + +class mekd1_state : public driver_device +{ +public: + mekd1_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_pia0(*this, "pia0") + , m_pia1(*this, "pia1") + , m_acia(*this, "acia") + , m_brg(*this, "brg") + , m_rs232(*this, "rs232") + , m_baud_rate(*this, "baud_rate") + , m_stop_bits(*this, "stop_bits") + , m_acia_baud_rate(*this, "acia_baud_rate") + { } + + void mekd1(machine_config &config); + + enum + { + TIMER_BIT_RATE, + TIMER_BIT_RATE_HALF + }; + +private: + virtual void machine_reset() override; + virtual void machine_start() override; + + void mem_map(address_map &map); + + required_device m_maincpu; + required_device m_pia0; + required_device m_pia1; + required_device m_acia; + required_device m_brg; + required_device m_rs232; + required_ioport m_baud_rate; + required_ioport m_stop_bits; + required_ioport m_acia_baud_rate; + + DECLARE_READ8_MEMBER(pia0_pa_r); + DECLARE_READ8_MEMBER(pia0_pb_r); + DECLARE_WRITE8_MEMBER(pia0_pa_w); + DECLARE_WRITE8_MEMBER(pia0_pb_w); + DECLARE_WRITE_LINE_MEMBER(pia0_cb2_w); + + // Clocks + DECLARE_WRITE_LINE_MEMBER(write_f1_clock); + DECLARE_WRITE_LINE_MEMBER(write_f2_clock); + DECLARE_WRITE_LINE_MEMBER(write_f4_clock); + DECLARE_WRITE_LINE_MEMBER(write_f5_clock); + DECLARE_WRITE_LINE_MEMBER(write_f7_clock); + DECLARE_WRITE_LINE_MEMBER(write_f8_clock); + DECLARE_WRITE_LINE_MEMBER(write_f9_clock); + DECLARE_WRITE_LINE_MEMBER(write_f11_clock); + DECLARE_WRITE_LINE_MEMBER(write_f13_clock); + + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + emu_timer *m_bit_rate_timer; + emu_timer *m_bit_rate_half_timer; + + bool m_bit_rate_out; + bool m_bit_rate_half_out; + bool m_bit_rate_reset; + bool m_bit_rate_select; +}; + +void mekd1_state::mem_map(address_map &map) +{ + map.unmap_value_high(); + map(0x0000, 0x027f).mirror(0x7c00).ram(); + + // The emulated addressing for these is not exact. The hardware uses + // A3, A4, and A5 to select between these respectively. For example + // the first PIA is selected when A3 is high, but it ignores A4 and + // A5, etc. So it is possible that more than one are selected at once + // which is not emulated here. Here it is assumed that A3, A4, and A5 + // are completely decoded and that only one device is selected at a + // time. + map(0x8004, 0x8007).mirror(0x5fe0).rw(m_pia0, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); + map(0x8008, 0x800b).mirror(0x5fe0).rw(m_pia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); + map(0x8010, 0x8011).mirror(0x5fe2).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); + + map(0xa000, 0xa07f).mirror(0x0f80).ram(); + + // Although the ROM is 1k, MIKBUG uses only the first 512 bytes and + // when using MIKBUG the A9 ROM input is connected to 0V. + map(0xe000, 0xe1ff).mirror(0x1e00).rom().region("mcm6830l7", 0); +} + +static INPUT_PORTS_START( mekd1 ) + + PORT_START("baud_rate") + PORT_CONFNAME(0x3fff, 416, "RS232 Baud Rate") + PORT_CONFSETTING(9091, "110") + PORT_CONFSETTING(3333, "300") + PORT_CONFSETTING(1667, "600") + PORT_CONFSETTING( 833, "1200") + PORT_CONFSETTING( 416, "2400") + PORT_CONFSETTING( 208, "4800") + PORT_CONFSETTING( 139, "7200") + PORT_CONFSETTING( 104, "9600") + + PORT_START("stop_bits") + PORT_CONFNAME(0x01, 0, "Stop bits") + PORT_CONFSETTING(0x00, "1") + PORT_CONFSETTING(0x01, "2") + + PORT_START("acia_baud_rate") + PORT_CONFNAME(0xf, 1, "ACIA Baud Rate") + PORT_CONFSETTING(13, "110") + PORT_CONFSETTING(11, "150") + PORT_CONFSETTING(9, "300") + PORT_CONFSETTING(8, "600") + PORT_CONFSETTING(7, "1200") + PORT_CONFSETTING(5, "2400") + PORT_CONFSETTING(4, "3600") + PORT_CONFSETTING(2, "4800") + PORT_CONFSETTING(1, "9600") + +INPUT_PORTS_END + + + +READ8_MEMBER(mekd1_state::pia0_pa_r) +{ + return m_rs232->rxd_r() << 7; +} + +READ8_MEMBER(mekd1_state::pia0_pb_r) +{ + bool timer_out; + uint8_t stop_bits = m_stop_bits->read(); + + if (m_bit_rate_select) + timer_out = m_bit_rate_out; + else + timer_out = m_bit_rate_half_out; + + return (timer_out << 7) | (stop_bits << 6); +} + +WRITE8_MEMBER(mekd1_state::pia0_pa_w) +{ + m_rs232->write_txd(BIT(data, 0)); +} + +WRITE8_MEMBER(mekd1_state::pia0_pb_w) +{ + m_bit_rate_select = BIT(data, 2); + + if (BIT(data, 0) == 1) + { + // Reset + m_bit_rate_timer->reset(); + m_bit_rate_half_timer->reset(); + m_bit_rate_out = 0; + m_bit_rate_half_out = 0; + m_bit_rate_reset = 1; + return; + } + + if (m_bit_rate_reset) + { + // Timer has just been taken out of reset. + m_bit_rate_reset = 0; + uint16_t delay = m_baud_rate->read(); + // An adjustment is subtracted from the delay for the CPU + // instruction processing paths as the CPU polls the timer + // output, and this would vary with the CPU clock. In + // hardware, variable resisters needed to be tuned to achieve + // the correct timing which was not hard at the lower baud + // rates. + m_bit_rate_timer->adjust(attotime::from_usec(delay - 31)); + m_bit_rate_half_timer->adjust(attotime::from_usec((delay / 2 ) - 31)); + } +} + +WRITE_LINE_MEMBER(mekd1_state::pia0_cb2_w) +{ + // This is a tape reader control line. +} + +void mekd1_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_BIT_RATE: + m_bit_rate_out = 1; + break; + case TIMER_BIT_RATE_HALF: + m_bit_rate_half_out = 1; + break; + default: + throw emu_fatalerror("Unknown id in mekd1_state::device_timer"); + } +} + + +WRITE_LINE_MEMBER(mekd1_state::write_f1_clock) +{ + if (m_acia_baud_rate->read() == 1) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f2_clock) +{ + if (m_acia_baud_rate->read() == 2) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f4_clock) +{ + if (m_acia_baud_rate->read() == 4) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f5_clock) +{ + if (m_acia_baud_rate->read() == 5) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f7_clock) +{ + if (m_acia_baud_rate->read() == 7) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f8_clock) +{ + if (m_acia_baud_rate->read() == 8) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f9_clock) +{ + if (m_acia_baud_rate->read() == 9) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f11_clock) +{ + if (m_acia_baud_rate->read() == 11) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + +WRITE_LINE_MEMBER(mekd1_state::write_f13_clock) +{ + if (m_acia_baud_rate->read() == 13) + { + m_acia->write_txc(state); + m_acia->write_rxc(state); + } +} + + +void mekd1_state::machine_reset() +{ + m_pia0->reset(); + m_pia1->reset(); + m_brg->rsa_w(CLEAR_LINE); + m_brg->rsb_w(ASSERT_LINE); + m_bit_rate_timer->reset(); + m_bit_rate_half_timer->reset(); + m_bit_rate_out = 0; + m_bit_rate_half_out = 0; + m_bit_rate_select = 0; + m_bit_rate_reset = 1; +} + +void mekd1_state::machine_start() +{ + // Allocate timers + m_bit_rate_timer = timer_alloc(TIMER_BIT_RATE); + m_bit_rate_half_timer = timer_alloc(TIMER_BIT_RATE_HALF); + + save_item(NAME(m_bit_rate_out)); + save_item(NAME(m_bit_rate_half_out)); + save_item(NAME(m_bit_rate_reset)); + save_item(NAME(m_bit_rate_select)); +} + +static DEVICE_INPUT_DEFAULTS_START(terminal) + DEVICE_INPUT_DEFAULTS("RS232_RXBAUD", 0xff, RS232_BAUD_2400) + DEVICE_INPUT_DEFAULTS("RS232_TXBAUD", 0xff, RS232_BAUD_2400) + DEVICE_INPUT_DEFAULTS("RS232_STARTBITS", 0xff, RS232_STARTBITS_1) + DEVICE_INPUT_DEFAULTS("RS232_DATABITS", 0xff, RS232_DATABITS_7) + DEVICE_INPUT_DEFAULTS("RS232_PARITY", 0xff, RS232_PARITY_SPACE) + DEVICE_INPUT_DEFAULTS("RS232_STOPBITS", 0xff, RS232_STOPBITS_1) +DEVICE_INPUT_DEFAULTS_END + +void mekd1_state::mekd1(machine_config &config) +{ + // The clock rate was adjustable from 100KHz to 1MHz. + M6800(config, m_maincpu, 2_MHz_XTAL / 2); + m_maincpu->set_addrmap(AS_PROGRAM, &mekd1_state::mem_map); + + INPUT_MERGER_ANY_HIGH(config, "mainirq").output_handler().set_inputline(m_maincpu, M6800_IRQ_LINE); + + // Terminal I/O. + // The design uses a 6820 which is slightly different to the emulated 6821. + // PA0 serial output. + // PA7 serial input. + // PB0 resets the rite rate timer when high. + // PB2 selects the bit rate timer output: 0 half time, 1 full time. + // PB6 jumper input: 0 for 1 stop bit, 1 for 2 stop bits. + // PB7 input from the bit rate timer that goes high after the timer period elapses. + // CB2 "reader control" output + // IRQA and IRQB are NC. + PIA6821(config, m_pia0, 0); + m_pia0->readpa_handler().set(FUNC(mekd1_state::pia0_pa_r)); + m_pia0->readpb_handler().set(FUNC(mekd1_state::pia0_pb_r)); + m_pia0->writepa_handler().set(FUNC(mekd1_state::pia0_pa_w)); + m_pia0->writepb_handler().set(FUNC(mekd1_state::pia0_pb_w)); + m_pia0->cb2_handler().set(FUNC(mekd1_state::pia0_cb2_w)); + + // User PIA. All the I/O lines are available at P2. + PIA6821(config, m_pia1, 0); + m_pia1->irqa_handler().set("mainirq", FUNC(input_merger_device::in_w<0>)); + m_pia1->irqb_handler().set("mainirq", FUNC(input_merger_device::in_w<1>)); + + // User ACIA. Available at P2. + // /CTS is pulled low. + // /DCD is pulled low. + ACIA6850(config, m_acia, 0); + m_acia->irq_handler().set("mainirq", FUNC(input_merger_device::in_w<2>)); + + // Off-board clock for the on-board MC6850. + MC14411(config, m_brg, XTAL(1'843'200)); + m_brg->out_f<1>().set(FUNC(mekd1_state::write_f1_clock)); + m_brg->out_f<2>().set(FUNC(mekd1_state::write_f2_clock)); + m_brg->out_f<4>().set(FUNC(mekd1_state::write_f4_clock)); + m_brg->out_f<4>().set(FUNC(mekd1_state::write_f5_clock)); + m_brg->out_f<7>().set(FUNC(mekd1_state::write_f7_clock)); + m_brg->out_f<8>().set(FUNC(mekd1_state::write_f8_clock)); + m_brg->out_f<9>().set(FUNC(mekd1_state::write_f9_clock)); + m_brg->out_f<11>().set(FUNC(mekd1_state::write_f11_clock)); + m_brg->out_f<13>().set(FUNC(mekd1_state::write_f13_clock)); + + RS232_PORT(config, m_rs232, default_rs232_devices, "terminal"); + m_rs232->set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); +} + + +/* ROM definition */ +ROM_START( mekd1 ) + ROM_REGION( 0x0200, "mcm6830l7", 0 ) + ROM_LOAD("mikbugv9.bin", 0x0000, 0x0200, CRC(f5ff896f) SHA1(32990115ad9eebe7a1a5a03b4b1ea83360b1820f)) +ROM_END + +/* Driver */ + +// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS +COMP( 1975, mekd1, 0, 0, mekd1, mekd1, mekd1_state, empty_init, "Motorola", "MEK6800D1", MACHINE_NO_SOUND_HW ) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index a63ee751fdd..5e73fa378cb 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -21524,6 +21524,9 @@ megazonej // GX319 (c) 1983 + Interlogic / Kosuka @source:meijinsn.cpp meijinsn // (c) 1986 SNK +@source:mekd1.cpp +mekd1 // 1975 Motorola Evaluation Kit + @source:mekd2.cpp mekd2 // 1977 Motorola Evaluation Kit diff --git a/src/mame/mess.flt b/src/mame/mess.flt index 91f9f9f0af8..1804bde49e1 100644 --- a/src/mame/mess.flt +++ b/src/mame/mess.flt @@ -463,6 +463,7 @@ mccpm.cpp mdisk.cpp megadriv.cpp megadriv_rad.cpp +mekd1.cpp mekd2.cpp mekd3.cpp mekd4.cpp