summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author 68bit <info@68bit.org>2019-10-06 20:27:09 +1100
committer Vas Crabb <cuavas@users.noreply.github.com>2019-10-08 03:47:02 +1100
commit92d7227ec0cde61a7283d8b303035e10ca502bab (patch)
tree5f3b161b0ed871968fbfdab73bc01c06800dee76
parent6cb51d56739c2ce96652a56a3142a6c3fdd09436 (diff)
MEK6800D1: early Motorola 6800 design evaluation board
The MIKBUG monitor is working. The terminal support for reading and 'punching' tapes is not yet implemented.
-rw-r--r--src/mame/drivers/mekd1.cpp475
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/mess.flt1
3 files changed, 479 insertions, 0 deletions
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'.
+ <space>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.
+ <space> 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<cpu_device> m_maincpu;
+ required_device<pia6821_device> m_pia0;
+ required_device<pia6821_device> m_pia1;
+ required_device<acia6850_device> m_acia;
+ required_device<mc14411_device> m_brg;
+ required_device<rs232_port_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