// license:GPL-2.0+ // copyright-holders:Juergen Buchmueller, Robbbert /****************************************************************************** Motorola Evaluation Kit 6800 D2 MEK6800D2 system driver Juergen Buchmueller, Jan 2000 2013-06-16 Working driver [Robbbert] memory map range short description 0000-00ff RAM 256 bytes RAM 0100-01ff RAM optional 256 bytes RAM 6000-63ff PROM optional PROM or 6000-67ff ROM optional ROM 8004-8007 PIA expansion port 8008-8009 ACIA cassette interface 8020-8023 PIA keyboard interface a000-a07f RAM 128 bytes RAM (JBUG scratch) c000-c3ff PROM optional PROM or c000-c7ff ROM optional ROM e000-e3ff ROM JBUG monitor program e400-ffff -/- mirrors of monitor rom Enter the 4 digit address then the command key: - M : Examine and Change Memory (example: E000M, then G to skip to next, ESC to exit) - E : Escape (abort) operation (ESC key in our emulation) - R : Examine Registers - G : Begin execution at specified address - P : Punch data from memory to magnetic tape - L : Load memory from magnetic tape - N : Trace one instruction - V : Set (and remove) breakpoints The keys are laid out as: P L N V 7 8 9 A M 4 5 6 B E 1 2 3 C R 0 F E D G Pasting: 0-F : as is NEXT : ^ MEM : = GO : ^ Test Paste: HA030=11^22^33^44^55^66^77^88^99^HA030= Now press up-arrow to confirm the data has been entered. If you wish to follow the tutorial in the manual, here is the test program that you need to enter in step 1: H0020=8E^00^FF^4F^C6^04^CE^00^10^AB^00^08^5A^26^FA^97^15^3F^H Save the above program to tape: HA002=00^20^00^32^HP (A002 has start address, A004 has end address, big endian) TODO Display should go blank during cassette operations ******************************************************************************/ #include "emu.h" #include "cpu/m6800/m6800.h" #include "imagedev/cassette.h" #include "imagedev/snapquik.h" #include "machine/6821pia.h" #include "machine/6850acia.h" #include "machine/clock.h" #include "machine/timer.h" #include "sound/wave.h" #include "speaker.h" #include "mekd2.lh" #define XTAL_MEKD2 1228800 class mekd2_state : public driver_device { public: enum { TIMER_TRACE }; mekd2_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") , m_pia_s(*this, "pia_s") , m_pia_u(*this, "pia_u") , m_acia(*this, "acia") , m_cass(*this, "cassette") , m_digits(*this, "digit%u", 0U) { } DECLARE_READ_LINE_MEMBER(mekd2_key40_r); DECLARE_READ8_MEMBER(mekd2_key_r); DECLARE_WRITE_LINE_MEMBER(mekd2_nmi_w); DECLARE_WRITE8_MEMBER(mekd2_digit_w); DECLARE_WRITE8_MEMBER(mekd2_segment_w); DECLARE_QUICKLOAD_LOAD_MEMBER(mekd2_quik); DECLARE_WRITE_LINE_MEMBER(cass_w); TIMER_DEVICE_CALLBACK_MEMBER(mekd2_c); TIMER_DEVICE_CALLBACK_MEMBER(mekd2_p); void mekd2(machine_config &config); void mekd2_mem(address_map &map); private: virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; uint8_t m_cass_data[4]; uint8_t m_segment; uint8_t m_digit; uint8_t m_keydata; bool m_cass_state; bool m_cassold; virtual void machine_start() override; required_device m_maincpu; required_device m_pia_s; required_device m_pia_u; required_device m_acia; required_device m_cass; output_finder<6> m_digits; }; /*********************************************************** Address Map ************************************************************/ void mekd2_state::mekd2_mem(address_map &map) { map(0x0000, 0x00ff).ram(); // user ram map(0x8004, 0x8007).rw(m_pia_u, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0x8008, 0x8009).rw(m_acia, FUNC(acia6850_device::read), FUNC(acia6850_device::write)); map(0x8020, 0x8023).rw(m_pia_s, FUNC(pia6821_device::read), FUNC(pia6821_device::write)); map(0xa000, 0xa07f).ram(); // system ram map(0xe000, 0xe3ff).rom().mirror(0x1c00); /* JBUG ROM */ } /*********************************************************** Keys ************************************************************/ static INPUT_PORTS_START( mekd2 ) PORT_START("X0") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E (hex)") PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_START("X1") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_START("X2") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_START("X3") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_START("X4") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P) PORT_CHAR('P') // save tape PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_CHAR('L') // load tape PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N) PORT_CHAR('N') // trace (step) PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V) PORT_CHAR('V') // breakpoint PORT_START("X5") PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M) PORT_CHAR('=') // memory PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E (escape)") PORT_CODE(KEYCODE_ESC) PORT_CHAR('H') PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R) PORT_CHAR('R') // regs PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G) PORT_CODE(KEYCODE_UP) PORT_CHAR('^') // go, next INPUT_PORTS_END /*********************************************************** Trace hardware (what happens when N is pressed) ************************************************************/ void mekd2_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch (id) { case TIMER_TRACE: m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); break; default: assert_always(false, "Unknown id in mekd2_state::device_timer"); } } WRITE_LINE_MEMBER( mekd2_state::mekd2_nmi_w ) { if (state) m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); else timer_set(attotime::from_usec(18), TIMER_TRACE); } /*********************************************************** Keyboard ************************************************************/ READ_LINE_MEMBER( mekd2_state::mekd2_key40_r ) { return BIT(m_keydata, 6); } READ8_MEMBER( mekd2_state::mekd2_key_r ) { char kbdrow[4]; uint8_t i; m_keydata = 0xff; for (i = 0; i < 6; i++) { if (BIT(m_digit, i)) { sprintf(kbdrow,"X%d",i); m_keydata &= ioport(kbdrow)->read(); } } i = 0x80; if (m_digit < 0x40) i = BIT(m_keydata, 0) ? 0x80 : 0; else if (m_digit < 0x80) i = BIT(m_keydata, 1) ? 0x80 : 0; else if (m_digit < 0xc0) i = BIT(m_keydata, 2) ? 0x80 : 0; else i = BIT(m_keydata, 3) ? 0x80 : 0; return i | m_segment; } /*********************************************************** LED display ************************************************************/ WRITE8_MEMBER( mekd2_state::mekd2_segment_w ) { m_segment = data & 0x7f; } WRITE8_MEMBER( mekd2_state::mekd2_digit_w ) { uint8_t i; if (data < 0x3f) { for (i = 0; i < 6; i++) { if (BIT(data, i)) m_digits[i] = ~m_segment & 0x7f; } } m_digit = data; } /*********************************************************** Interfaces ************************************************************/ WRITE_LINE_MEMBER( mekd2_state::cass_w ) { m_cass_state = state; } QUICKLOAD_LOAD_MEMBER( mekd2_state, mekd2_quik ) { static const char magic[] = "MEK6800D2"; char buff[9]; uint16_t addr, size; uint8_t ident, *RAM = memregion("maincpu")->base(); image.fread(buff, sizeof (buff)); if (memcmp(buff, magic, sizeof (buff))) { logerror("mekd2 rom load: magic '%s' not found\n", magic); return image_init_result::FAIL; } image.fread(&addr, 2); addr = little_endianize_int16(addr); image.fread(&size, 2); size = little_endianize_int16(size); image.fread(&ident, 1); logerror("mekd2 rom load: $%04X $%04X $%02X\n", addr, size, ident); while (size-- > 0) image.fread(&RAM[addr++], 1); return image_init_result::PASS; } TIMER_DEVICE_CALLBACK_MEMBER(mekd2_state::mekd2_c) { m_cass_data[3]++; if (m_cass_state != m_cassold) { m_cass_data[3] = 0; m_cassold = m_cass_state; } if (m_cass_state) m_cass->output(BIT(m_cass_data[3], 0) ? -1.0 : +1.0); // 2400Hz else m_cass->output(BIT(m_cass_data[3], 1) ? -1.0 : +1.0); // 1200Hz } TIMER_DEVICE_CALLBACK_MEMBER(mekd2_state::mekd2_p) { /* cassette - turn 1200/2400Hz to a bit */ m_cass_data[1]++; uint8_t cass_ws = (m_cass->input() > +0.03) ? 1 : 0; if (cass_ws != m_cass_data[0]) { m_cass_data[0] = cass_ws; m_acia->write_rxd((m_cass_data[1] < 12) ? 1 : 0); m_cass_data[1] = 0; } } void mekd2_state::machine_start() { m_digits.resolve(); } /*********************************************************** Machine ************************************************************/ MACHINE_CONFIG_START(mekd2_state::mekd2) /* basic machine hardware */ MCFG_DEVICE_ADD("maincpu", M6800, XTAL_MEKD2 / 2) /* 614.4 kHz */ MCFG_DEVICE_PROGRAM_MAP(mekd2_mem) MCFG_DEFAULT_LAYOUT(layout_mekd2) /* sound hardware */ SPEAKER(config, "mono").front_center(); WAVE(config, "wave", "cassette").add_route(ALL_OUTPUTS, "mono", 0.25); MCFG_CASSETTE_ADD("cassette") /* Devices */ MCFG_DEVICE_ADD("pia_s", PIA6821, 0) MCFG_PIA_READPA_HANDLER(READ8(*this, mekd2_state, mekd2_key_r)) MCFG_PIA_READCB1_HANDLER(READLINE(*this, mekd2_state, mekd2_key40_r)) MCFG_PIA_WRITEPA_HANDLER(WRITE8(*this, mekd2_state, mekd2_segment_w)) MCFG_PIA_WRITEPB_HANDLER(WRITE8(*this, mekd2_state, mekd2_digit_w)) MCFG_PIA_CA2_HANDLER(WRITELINE(*this, mekd2_state, mekd2_nmi_w)) MCFG_PIA_IRQA_HANDLER(INPUTLINE("maincpu", INPUT_LINE_NMI)) MCFG_PIA_IRQB_HANDLER(INPUTLINE("maincpu", INPUT_LINE_NMI)) MCFG_DEVICE_ADD("pia_u", PIA6821, 0) MCFG_PIA_IRQA_HANDLER(INPUTLINE("maincpu", M6800_IRQ_LINE)) MCFG_PIA_IRQB_HANDLER(INPUTLINE("maincpu", M6800_IRQ_LINE)) MCFG_DEVICE_ADD("acia", ACIA6850, 0) MCFG_ACIA6850_TXD_HANDLER(WRITELINE(*this, mekd2_state, cass_w)) MCFG_DEVICE_ADD("acia_tx_clock", CLOCK, XTAL_MEKD2 / 256) // 4800Hz MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE("acia", acia6850_device, write_txc)) MCFG_DEVICE_ADD("acia_rx_clock", CLOCK, 300) // toggled by cassette circuit MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE("acia", acia6850_device, write_rxc)) MCFG_TIMER_DRIVER_ADD_PERIODIC("mekd2_c", mekd2_state, mekd2_c, attotime::from_hz(4800)) MCFG_TIMER_DRIVER_ADD_PERIODIC("mekd2_p", mekd2_state, mekd2_p, attotime::from_hz(40000)) MCFG_QUICKLOAD_ADD("quickload", mekd2_state, mekd2_quik, "d2", 1) MACHINE_CONFIG_END /*********************************************************** ROMS ************************************************************/ ROM_START(mekd2) ROM_REGION(0x10000,"maincpu",0) ROM_LOAD("jbug.rom", 0xe000, 0x0400, CRC(5ed08792) SHA1(b06e74652a4c4e67c4a12ddc191ffb8c07f3332e) ) ROM_END /*************************************************************************** Game driver(s) ***************************************************************************/ // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS COMP( 1977, mekd2, 0, 0, mekd2, mekd2, mekd2_state, empty_init, "Motorola", "MEK6800D2" , 0 )