summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/drivers/mmd2.cpp
blob: be145348304d4a411518e25276740034a560c0cb (plain) (tree)
1
2
3
4
5
6
7
8






                                                                            
                                  



































































































                                                                                                              
                                              
                                                
                                        
                                                




                                               






                                                   



                                              
















                                                 
                           
 
                        

                                                      
                                              
                                               
                                 
                                  



                                   




                                     







                                        



                                     







                                        



                                     







                                        

































































































                                                                                                                                                             


                                        





                                    
                                 

































                                                      
                                                           

                                                                        
                                                        




                                              









                                








                                

                                   



                            


























                                                                                                          









































                                                                                                                                 
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic, Robbbert
/***************************************************************************

MMD-2 driver by Miodrag Milanovic

2009-05-12 Initial version
2011-01-12 MMD2 working [Robbbert]


http://www.cs.unc.edu/~yakowenk/classiccmp/mmd2/
Memory map:

    * 4K RAM addresses $0000..$0FFF
    * ROM addresses $D800..$E7FF
    * 256 bytes of RAM, ($FC00..$FCFF?)

DIP switches:

    * WE 0 - Write enable for addresses $0000..$03FF
    * WE 1 - Write enable for addresses $0400..$07FF
    * WE 2 - Write enable for addresses $0800..$0BFF
    * WE 3 - Write enable for addresses $0C00..$0FFF
    * SPARE - ???
    * HEX OCT - choose display and entry to be in Hexadecimal or Octal
    * PUP RESET - ???
    * EXEC USER - update binary LED's with data entry? Or not?
      (in either setting, outputs to ports 0,1,2 still show)

Operation:

    * Enter bytes on the keypad of hex digits
    * Set MSByte of address by entering it on the keypad & pressing "HIGH".
    * ... LSByte ... "LOW"
    * Change contents of memory at the selected address by entering the new value & pressing "STORE".
    * Look at adjacent memory locations with "NEXT" and "PREV".
    * Execute the program at the selected address by pressing "GO".

AUX functions:

    * BRL HI # - OFF disables BRL LO
    * BRL LO #
    * STEP #
    * SRC HI # - source for COPY/DUMP - OFF disables "DUMP" function
    * DES HI # - destination for COPY/DUMP
    * LEN HI # - length for COPY/DUMP
    * CLR TST ON - test if PROM is empty
    * POP PRM ON - program a PROM
    * DUP TST ON - test if PROM duplicated okay
    * PROM 2708/2716
    * MEM MAP RAM/ROM
    * BAUD 110/150/300/600/1200

The memory map can be rearranged by the system by using IN5, IN6, IN7.
A pair of undumped proms control what goes where on each map.
Each set of ROMs also has its own pair of PROMs.


I/O ports:
IN0: user expansion
IN1: 0-TTYIN, 1-CASSIN, 3-SW8(binary/ports), 4-SW7(reset/pup), 5-SW6(hex/oct), 6-(pup signal)
IN3: 8279 status
IN4: 8279 key
IN5: set MAP1
IN6: set MAP2
IN7: set MAP3
IN8: read eprom (in the eprom programmer)
OUT0: PORT0 LEDs
OUT1: PORT1 LEDs
OUT2: PORT2 LEDs
OUT3: 8279 control
OUT4: 8279 7-segment LED data
OUT5: TTYOUT, CASSOUT
OUT9: turn pup signal off
OUTA: programming pulse on/off (eprom programmer)

Dips:
SW1-4 hardware control of RAM being writable
SW5 not used
SW6 Control if the 7-segment displays show Octal (low) or Hex (high).
SW7 Reset only causes the cpu to perform a warm start. PUP does a cold start (Power-UP).
SW8 Control if the PORT displays echo the 7-segment displays (high), or just act as normal output ports (low).


ToDo
- Hook up WE0-3
- tty rs232 interface
- Add interrupt module (INTE LED is always on atm)
- Need software
- Probably lots of other things

****************************************************************************/

#include "emu.h"
#include "cpu/i8085/i8085.h"
#include "machine/i8279.h"
#include "imagedev/cassette.h"
#include "speaker.h"
#include "mmd2.lh"


class mmd2_state : public driver_device
{
public:
	mmd2_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")
		, m_cass(*this, "cassette")
		, m_banks(*this, "bank%u", 1U)
		, m_io_keyboard(*this, "X%u", 0)
		, m_io_dsw(*this, "DSW")
		, m_digits(*this, "digit%u", 0U)
		, m_p(*this, "p%u_%u", 0U, 0U)
		, m_led_halt(*this, "led_halt")
		, m_led_hold(*this, "led_hold")
		, m_led_inte(*this, "led_inte")
	{ }

	void mmd2(machine_config &config);

	void init_mmd2();

	DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;

private:
	DECLARE_WRITE8_MEMBER(port00_w);
	DECLARE_WRITE8_MEMBER(port01_w);
	DECLARE_WRITE8_MEMBER(port02_w);
	DECLARE_WRITE8_MEMBER(port05_w);
	DECLARE_READ8_MEMBER(port01_r);
	DECLARE_READ8_MEMBER(port13_r);
	DECLARE_READ8_MEMBER(bank_r);
	DECLARE_READ8_MEMBER(keyboard_r);
	DECLARE_WRITE8_MEMBER(scanlines_w);
	DECLARE_WRITE8_MEMBER(digit_w);
	DECLARE_WRITE8_MEMBER(status_callback);
	DECLARE_WRITE_LINE_MEMBER(inte_callback);
	DECLARE_READ_LINE_MEMBER(si);
	DECLARE_WRITE_LINE_MEMBER(so);
	void io_map(address_map &map);
	void mem_map(address_map &map);
	void reset_banks();

	uint8_t m_digit;
	required_device<i8080_cpu_device> m_maincpu;
	required_device<cassette_image_device> m_cass;
	required_memory_bank_array<8> m_banks;
	required_ioport_array<4> m_io_keyboard;
	required_ioport m_io_dsw;
	output_finder<9> m_digits;
	output_finder<3, 8> m_p;
	output_finder<> m_led_halt;
	output_finder<> m_led_hold;
	output_finder<> m_led_inte;
};


WRITE8_MEMBER( mmd2_state::port00_w )
{
	m_p[0][7] = BIT(data,7) ? 0 : 1;
	m_p[0][6] = BIT(data,6) ? 0 : 1;
	m_p[0][5] = BIT(data,5) ? 0 : 1;
	m_p[0][4] = BIT(data,4) ? 0 : 1;
	m_p[0][3] = BIT(data,3) ? 0 : 1;
	m_p[0][2] = BIT(data,2) ? 0 : 1;
	m_p[0][1] = BIT(data,1) ? 0 : 1;
	m_p[0][0] = BIT(data,0) ? 0 : 1;
}

WRITE8_MEMBER( mmd2_state::port01_w )
{
	m_p[1][7] = BIT(data,7) ? 0 : 1;
	m_p[1][6] = BIT(data,6) ? 0 : 1;
	m_p[1][5] = BIT(data,5) ? 0 : 1;
	m_p[1][4] = BIT(data,4) ? 0 : 1;
	m_p[1][3] = BIT(data,3) ? 0 : 1;
	m_p[1][2] = BIT(data,2) ? 0 : 1;
	m_p[1][1] = BIT(data,1) ? 0 : 1;
	m_p[1][0] = BIT(data,0) ? 0 : 1;
}

WRITE8_MEMBER( mmd2_state::port02_w )
{
	m_p[2][7] = BIT(data,7) ? 0 : 1;
	m_p[2][6] = BIT(data,6) ? 0 : 1;
	m_p[2][5] = BIT(data,5) ? 0 : 1;
	m_p[2][4] = BIT(data,4) ? 0 : 1;
	m_p[2][3] = BIT(data,3) ? 0 : 1;
	m_p[2][2] = BIT(data,2) ? 0 : 1;
	m_p[2][1] = BIT(data,1) ? 0 : 1;
	m_p[2][0] = BIT(data,0) ? 0 : 1;
}

void mmd2_state::mem_map(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x03ff).bankr("bank1").bankw("bank2");
	map(0x0400, 0x0fff).bankr("bank3").bankw("bank4");
	map(0xd800, 0xe3ff).bankr("bank5").bankw("bank6");
	map(0xe400, 0xe7ff).bankr("bank7").bankw("bank8");
	map(0xfc00, 0xfcff).ram(); // Scratchpad
}

void mmd2_state::io_map(address_map &map)
{
	map.unmap_value_high();
	map(0x00, 0x00).w(FUNC(mmd2_state::port00_w));
	map(0x01, 0x01).rw(FUNC(mmd2_state::port01_r), FUNC(mmd2_state::port01_w));
	map(0x02, 0x02).w(FUNC(mmd2_state::port02_w));
	map(0x03, 0x03).rw("i8279", FUNC(i8279_device::status_r), FUNC(i8279_device::cmd_w));
	map(0x04, 0x04).rw("i8279", FUNC(i8279_device::data_r), FUNC(i8279_device::data_w));
	map(0x05, 0x07).r(FUNC(mmd2_state::bank_r));
	map(0x05, 0x05).w(FUNC(mmd2_state::port05_w));
	//map(0x09, 0x09).w  PUP signal
	//map(0x0a, 0x0a).w  Eprom programmer
}


/* Input ports */
static INPUT_PORTS_START( mmd2 )
	PORT_START("DSW")
	PORT_DIPNAME( 0x20, 0x00, "Sw6") PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x20, "Hex")
	PORT_DIPSETTING(    0x00, "Octal")
	PORT_DIPNAME( 0x10, 0x10, "Sw7") PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x10, "PUP")
	PORT_DIPSETTING(    0x00, "Reset")
	PORT_DIPNAME( 0x08, 0x08, "Sw8") PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x08, "Exec")
	PORT_DIPSETTING(    0x00, "User")

	PORT_START("X0")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CANCEL") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("AUX") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("REGS") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MEM") PORT_CODE(KEYCODE_MINUS)
	PORT_START("X1")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("COPY") PORT_CODE(KEYCODE_Y)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PROM") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DUMP") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOAD") PORT_CODE(KEYCODE_O)
	PORT_START("X2")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("OPTION") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GO") PORT_CODE(KEYCODE_G)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LOW") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("HIGH") PORT_CODE(KEYCODE_H)
	PORT_START("X3")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STEP") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NEXT") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("STORE") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("PREV") PORT_CODE(KEYCODE_DOWN)
	PORT_START("RESET")
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("RESET") PORT_CODE(KEYCODE_LALT) PORT_CHANGED_MEMBER(DEVICE_SELF, mmd2_state, reset_button, 0)
INPUT_PORTS_END

INPUT_CHANGED_MEMBER(mmd2_state::reset_button)
{
	if (newval)
		reset_banks();
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}

/*
Keyboard
0  1  2  3      PREV  STORE  NEXT  STEP
4  5  6  7      HIGH  LOW  GO  OPTION
8  9  A  B      LOAD  DUMP  PROM  COPY
C  D  E  F      MEM  REGS  AUX  CANCEL

*/

READ8_MEMBER( mmd2_state::bank_r )
{
	for (auto &bank : m_banks)
		bank->set_entry(offset);
	return space.unmap();
}

READ8_MEMBER( mmd2_state::port01_r )
{
	// need to add ttyin bit 0
	uint8_t data = 0x84;
	data |= m_io_dsw->read();
	data |= (m_cass->input() < 0.02) ? 0 : 2;
	return data;
}

WRITE8_MEMBER( mmd2_state::port05_w )
{
	// need to add ttyout bit 0
	m_cass->output(BIT(data, 1) ? -1.0 : +1.0);
}

WRITE8_MEMBER( mmd2_state::scanlines_w )
{
	m_digit = data;
}

WRITE8_MEMBER( mmd2_state::digit_w )
{
	if (m_digit < 9)
		m_digits[m_digit] = data;
}

READ8_MEMBER( mmd2_state::keyboard_r )
{
	uint8_t data = 0xff;

	if (m_digit < 4)
		data = m_io_keyboard[m_digit]->read();

	return data;
}

WRITE8_MEMBER( mmd2_state::status_callback )
{
	// operate the HALT LED
	m_led_halt = ~data & i8080_cpu_device::STATUS_HLTA;
	// operate the HOLD LED - this should connect to the HLDA pin,
	// but it isn't emulated, using WO instead (whatever that does).
	m_led_hold = data & i8080_cpu_device::STATUS_WO;
}

WRITE_LINE_MEMBER( mmd2_state::inte_callback )
{
	// operate the INTE LED
	m_led_inte = state;
}

void mmd2_state::machine_start()
{
	m_digits.resolve();
	m_p.resolve();
	m_led_halt.resolve();
	m_led_hold.resolve();
	m_led_inte.resolve();
}

void mmd2_state::machine_reset()
{
	reset_banks();
}

void mmd2_state::reset_banks()
{
	for (auto &bank : m_banks)
		bank->set_entry(0);
}

void mmd2_state::init_mmd2()
{
	// We preset all banks here, so that bankswitching will incur no speed penalty.
	// 0000/0400 indicate ROMs, D800/DC00/E400 indicate RAM, 8000 is a dummy write area for ROM banks.
	uint8_t *const p_ram = memregion("maincpu")->base();
	m_banks[0]->configure_entry(0, &p_ram[0x0000]);
	m_banks[0]->configure_entry(1, &p_ram[0xd800]);
	m_banks[0]->configure_entry(2, &p_ram[0x0c00]);
	m_banks[1]->configure_entry(0, &p_ram[0x8000]);
	m_banks[1]->configure_entry(1, &p_ram[0xd800]);
	m_banks[1]->configure_entry(2, &p_ram[0x8000]);
	m_banks[2]->configure_entry(0, &p_ram[0x0400]);
	m_banks[2]->configure_entry(1, &p_ram[0xdc00]);
	m_banks[2]->configure_entry(2, &p_ram[0xdc00]);
	m_banks[3]->configure_entry(0, &p_ram[0x8000]);
	m_banks[3]->configure_entry(1, &p_ram[0xdc00]);
	m_banks[3]->configure_entry(2, &p_ram[0xdc00]);
	m_banks[4]->configure_entry(0, &p_ram[0xd800]);
	m_banks[4]->configure_entry(1, &p_ram[0x0000]);
	m_banks[4]->configure_entry(2, &p_ram[0x0000]);
	m_banks[5]->configure_entry(0, &p_ram[0xd800]);
	m_banks[5]->configure_entry(1, &p_ram[0x8000]);
	m_banks[5]->configure_entry(2, &p_ram[0x8000]);
	m_banks[6]->configure_entry(0, &p_ram[0xe400]);
	m_banks[6]->configure_entry(1, &p_ram[0x0c00]);
	m_banks[6]->configure_entry(2, &p_ram[0xd800]);
	m_banks[7]->configure_entry(0, &p_ram[0xe400]);
	m_banks[7]->configure_entry(1, &p_ram[0x8000]);
	m_banks[7]->configure_entry(2, &p_ram[0xd800]);
}

void mmd2_state::mmd2(machine_config &config)
{
	/* basic machine hardware */
	I8080(config, m_maincpu, 6750000 / 9);
	m_maincpu->set_addrmap(AS_PROGRAM, &mmd2_state::mem_map);
	m_maincpu->set_addrmap(AS_IO, &mmd2_state::io_map);
	m_maincpu->out_status_func().set(FUNC(mmd2_state::status_callback));
	m_maincpu->out_inte_func().set(FUNC(mmd2_state::inte_callback));

	/* video hardware */
	config.set_default_layout(layout_mmd2);

	/* Devices */
	i8279_device &kbdc(I8279(config, "i8279", 400000));             // based on divider
	kbdc.out_sl_callback().set(FUNC(mmd2_state::scanlines_w)); // scan SL lines
	kbdc.out_disp_callback().set(FUNC(mmd2_state::digit_w));   // display A&B
	kbdc.in_rl_callback().set(FUNC(mmd2_state::keyboard_r));        // kbd RL lines
	kbdc.in_shift_callback().set_constant(1);                       // Shift key
	kbdc.in_ctrl_callback().set_constant(1);

	// Cassette
	CASSETTE(config, m_cass);
	m_cass->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_ENABLED);
	SPEAKER(config, "mono").front_center();
	m_cass->add_route(ALL_OUTPUTS, "mono", 0.05);
}

/* ROM definition */
ROM_START( mmd2 )
	ROM_REGION( 0x10000, "maincpu", ROMREGION_ERASEFF )
	ROM_LOAD( "mmd2330.bin", 0x0000, 0x0800, CRC(69a77199) SHA1(6c83093b2c32a558c969f4fe8474b234023cc348))
	ROM_LOAD( "mmd2340.bin", 0x0800, 0x0800, CRC(70681bd6) SHA1(c37e3cf34a75e8538471030bb49b8aed45d00ec3))
	ROM_LOAD( "mmd2350.bin", 0x1000, 0x0800, CRC(359f577c) SHA1(9405ca0c1977721e4540a4017907c06dab08d398))
	ROM_LOAD( "mmd2360.bin", 0x1800, 0x0800, CRC(967e69b8) SHA1(c21ec8bef955806a2c6e1b1c8e9068662fb88038))
ROM_END

/* Driver */

//    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  CLASS       INIT        COMPANY                FULLNAME  FLAGS
COMP( 1976, mmd2,  mmd1,   0,      mmd2,    mmd2,  mmd2_state, init_mmd2,  "E&L Instruments Inc", "MMD-2",  MACHINE_NO_SOUND_HW )