MEK6800D1: early Motorola 6800 design evaluation board

The MIKBUG monitor is working. The terminal support for reading and
'punching' tapes is not yet implemented.
This commit is contained in:
68bit 2019-10-06 20:27:09 +11:00 committed by Vas Crabb
parent 6cb51d5673
commit 92d7227ec0
3 changed files with 479 additions and 0 deletions

475
src/mame/drivers/mekd1.cpp Normal file
View File

@ -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 )

View File

@ -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

View File

@ -463,6 +463,7 @@ mccpm.cpp
mdisk.cpp
megadriv.cpp
megadriv_rad.cpp
mekd1.cpp
mekd2.cpp
mekd3.cpp
mekd4.cpp