// license:BSD-3-Clause
// copyright-holders:Curt Coder
/***************************************************************************

        Sage II

        For memory map look at :
            http://www.thebattles.net/sage/img/SDT.pdf  (pages 14-)


        06/12/2009 Skeleton driver.

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

/*

    TODO:

    - floppy loading
    - TMS9914 IEEE-488 controller
    - board 2 (4x 2651 USART)
    - Winchester controller

*/

#include "includes/sage2.h"


//**************************************************************************
//  MEMORY MANAGEMENT UNIT
//**************************************************************************

//-------------------------------------------------
//  read -
//-------------------------------------------------

READ8_MEMBER( sage2_state::read )
{
	UINT8 data = 0xff;

	if (m_reset || (offset >= 0xfe0000 && offset < 0xff4000))
	{
		data = m_rom[offset & 0x1fff];
	}
	else if (offset < 0x080000)
	{
		data = m_ram->pointer()[offset];
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE8_MEMBER( sage2_state::write )
{
	if (offset < 0x080000)
	{
		m_ram->pointer()[offset] = data;
	}
}



//**************************************************************************
//  ADDRESS MAPS
//**************************************************************************

//-------------------------------------------------
//  ADDRESS_MAP( sage2_mem )
//-------------------------------------------------

static ADDRESS_MAP_START( sage2_mem, AS_PROGRAM, 16, sage2_state )
	ADDRESS_MAP_UNMAP_HIGH
	AM_RANGE(0x000000, 0xfeffff) AM_READWRITE8(read, write, 0xffff)
	AM_RANGE(0xffc000, 0xffc007) AM_DEVREADWRITE8(I8253_1_TAG, pit8253_device, read, write, 0x00ff)
	AM_RANGE(0xffc010, 0xffc01f) AM_NOP //AM_DEVREADWRITE8(TMS9914_TAG, tms9914_device, read, write, 0x00ff)
	AM_RANGE(0xffc020, 0xffc027) AM_DEVREADWRITE8(I8255A_0_TAG, i8255_device, read, write, 0x00ff) // i8255, DIPs + Floppy ctrl port
	AM_RANGE(0xffc030, 0xffc031) AM_DEVREADWRITE8(I8251_1_TAG, i8251_device, data_r, data_w, 0x00ff)
	AM_RANGE(0xffc032, 0xffc033) AM_DEVREADWRITE8(I8251_1_TAG, i8251_device, status_r, control_w, 0x00ff)
	AM_RANGE(0xffc040, 0xffc043) AM_DEVREADWRITE8(I8259_TAG, pic8259_device, read, write, 0x00ff)
	AM_RANGE(0xffc050, 0xffc053) AM_DEVICE8(UPD765_TAG, upd765a_device, map, 0x00ff)
	AM_RANGE(0xffc060, 0xffc067) AM_DEVREADWRITE8(I8255A_1_TAG, i8255_device, read, write, 0x00ff) // i8255, Printer
	AM_RANGE(0xffc070, 0xffc071) AM_DEVREADWRITE8(I8251_0_TAG, i8251_device, data_r, data_w, 0x00ff)
	AM_RANGE(0xffc072, 0xffc073) AM_DEVREADWRITE8(I8251_0_TAG, i8251_device, status_r, control_w, 0x00ff)
	AM_RANGE(0xffc080, 0xffc087) AM_MIRROR(0x78) AM_DEVREADWRITE8(I8253_0_TAG, pit8253_device, read, write, 0x00ff)
//  AM_RANGE(0xffc400, 0xffc407) AM_DEVREADWRITE8(S2651_0_TAG, s2651_device, read, write, 0x00ff)
//  AM_RANGE(0xffc440, 0xffc447) AM_DEVREADWRITE8(S2651_1_TAG, s2651_device, read, write, 0x00ff)
//  AM_RANGE(0xffc480, 0xffc487) AM_DEVREADWRITE8(S2651_2_TAG, s2651_device, read, write, 0x00ff)
//  AM_RANGE(0xffc4c0, 0xffc4c7) AM_DEVREADWRITE8(S2651_3_TAG, s2651_device, read, write, 0x00ff)
//  AM_RANGE(0xffc500, 0xffc7ff) // Winchester drive ports
ADDRESS_MAP_END



//**************************************************************************
//  INPUT PORTS
//**************************************************************************

//-------------------------------------------------
//  INPUT_PORTS( sage2 )
//-------------------------------------------------

static INPUT_PORTS_START( sage2 )
	PORT_START("J7")
	PORT_DIPNAME( 0x07, 0x07, "Terminal Baud Rate" ) PORT_DIPLOCATION("J7:1,2,3")
	PORT_DIPSETTING(    0x07, "19200" )
	PORT_DIPSETTING(    0x06, "9600" )
	PORT_DIPSETTING(    0x05, "4800" )
	PORT_DIPSETTING(    0x04, "2400" )
	PORT_DIPSETTING(    0x03, "1200" )
	PORT_DIPSETTING(    0x02, "600" )
	PORT_DIPSETTING(    0x01, "300" )
	PORT_DIPSETTING(    0x00, "Reserved (19200)" )
	PORT_DIPNAME( 0x08, 0x08, "Parity Control" ) PORT_DIPLOCATION("J7:4")
	PORT_DIPSETTING(    0x08, "Even Parity" )
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPNAME( 0x30, 0x20, "Boot Device" ) PORT_DIPLOCATION("J7:5,6")
	PORT_DIPSETTING(    0x30, "Debugger" )
	PORT_DIPSETTING(    0x20, "Floppy Drive 0" )
	PORT_DIPSETTING(    0x10, "Winchester" )
	PORT_DIPSETTING(    0x00, "Reserved (Debugger)" )
	PORT_DIPNAME( 0x40, 0x40, "Floppy Configuration" ) PORT_DIPLOCATION("J7:7")
	PORT_DIPSETTING(    0x40, "96 TPI" )
	PORT_DIPSETTING(    0x00, "48 TPI" )
	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Service_Mode ) ) PORT_DIPLOCATION("J7:8")
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

	PORT_START("J6")
	PORT_DIPNAME( 0x1f, 0x07, "IEEE-488 Bus Address" ) PORT_DIPLOCATION("J6:1,2,3,4,5")
	PORT_DIPSETTING(    0x00, "0" )
	PORT_DIPSETTING(    0x01, "1" )
	PORT_DIPSETTING(    0x02, "2" )
	PORT_DIPSETTING(    0x03, "3" )
	PORT_DIPSETTING(    0x04, "4" )
	PORT_DIPSETTING(    0x05, "5" )
	PORT_DIPSETTING(    0x06, "6" )
	PORT_DIPSETTING(    0x07, "7" )
	PORT_DIPSETTING(    0x08, "8" )
	PORT_DIPSETTING(    0x09, "9" )
	PORT_DIPSETTING(    0x0a, "10" )
	PORT_DIPSETTING(    0x0b, "11" )
	PORT_DIPSETTING(    0x0c, "12" )
	PORT_DIPSETTING(    0x0d, "13" )
	PORT_DIPSETTING(    0x0e, "14" )
	PORT_DIPSETTING(    0x0f, "15" )
	PORT_DIPSETTING(    0x10, "16" )
	PORT_DIPSETTING(    0x11, "17" )
	PORT_DIPSETTING(    0x12, "18" )
	PORT_DIPSETTING(    0x13, "19" )
	PORT_DIPSETTING(    0x14, "20" )
	PORT_DIPSETTING(    0x15, "21" )
	PORT_DIPSETTING(    0x16, "22" )
	PORT_DIPSETTING(    0x17, "23" )
	PORT_DIPSETTING(    0x18, "24" )
	PORT_DIPSETTING(    0x19, "25" )
	PORT_DIPSETTING(    0x1a, "26" )
	PORT_DIPSETTING(    0x1b, "27" )
	PORT_DIPSETTING(    0x1c, "28" )
	PORT_DIPSETTING(    0x1d, "29" )
	PORT_DIPSETTING(    0x1e, "30" )
	PORT_DIPSETTING(    0x1f, "31" )
	PORT_DIPNAME( 0x20, 0x00, "IEEE-488 TALK" ) PORT_DIPLOCATION("J6:6")
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPSETTING(    0x20, "Enabled" )
	PORT_DIPNAME( 0x40, 0x00, "IEEE-488 LISTEN" ) PORT_DIPLOCATION("J6:7")
	PORT_DIPSETTING(    0x00, "Disabled" )
	PORT_DIPSETTING(    0x40, "Enabled" )
	PORT_DIPNAME( 0x80, 0x00, "IEEE-488 Consecutive Adresses" ) PORT_DIPLOCATION("J6:8")
	PORT_DIPSETTING(    0x00, "1" )
	PORT_DIPSETTING(    0x80, "2" )
INPUT_PORTS_END



//**************************************************************************
//  DEVICE CONFIGURATION
//**************************************************************************

//-------------------------------------------------
//  I8255A_INTERFACE( ppi0_intf )
//-------------------------------------------------

/*

    IR0     U74 OUT2
    IR1     RX2I+
    IR2     TX1I+
    IR3     TX2I+
    IR4     MI-
    IR5     CNI+
    IR6     U74 OUT0
    IR7     SI+

*/


//-------------------------------------------------
//  I8255A_INTERFACE( ppi0_intf )
//-------------------------------------------------

WRITE8_MEMBER( sage2_state::ppi0_pc_w )
{
	/*

	    bit     signal

	    PC0     TC+
	    PC1     RDY+
	    PC2     FDIE+
	    PC3     SL0-
	    PC4     SL1-
	    PC5     MOT-
	    PC6     PCRMP-
	    PC7     FRES+

	*/

	// floppy terminal count
	m_fdc->tc_w(BIT(data, 0));

	// floppy ready
	m_fdc->ready_w(BIT(data, 1));

	// floppy interrupt enable
	m_fdie = BIT(data, 2);
	update_fdc_int();

	// drive select
	m_floppy = NULL;

	if (!BIT(data, 3)) m_floppy = m_floppy0->get_device();
	if (!BIT(data, 4)) m_floppy = m_floppy1->get_device();

	m_fdc->set_floppy(m_floppy);

	// floppy motor
	if (m_floppy) m_floppy->mon_w(BIT(data, 5));

	// FDC reset
	if(BIT(data, 7)) m_fdc->reset();
}

static I8255A_INTERFACE( ppi0_intf )
{
	DEVCB_INPUT_PORT("J7"),
	DEVCB_NULL,
	DEVCB_INPUT_PORT("J6"),
	DEVCB_NULL,
	DEVCB_NULL,
	DEVCB_DRIVER_MEMBER(sage2_state, ppi0_pc_w)
};


//-------------------------------------------------
//  I8255A_INTERFACE( ppi1_intf )
//-------------------------------------------------

READ8_MEMBER( sage2_state::ppi1_pb_r )
{
	/*

	    bit     signal

	    PB0     FDI+
	    PB1     WP+
	    PB2     RG-
	    PB3     CD-
	    PB4     BUSY
	    PB5     PAPER
	    PB6     SEL
	    PB7     FAULT-

	*/

	UINT8 data = 0;

	// floppy interrupt
	data = m_fdc->get_irq();

	// floppy write protected
	data = (m_floppy ? m_floppy->wpt_r() : 1) << 1;

	// RS-232 ring indicator

	// RS-232 carrier detect

	// centronics
	data |= m_centronics->busy_r() << 4;
	data |= m_centronics->pe_r() << 5;
	data |= m_centronics->vcc_r() << 6;
	data |= m_centronics->fault_r() << 7;

	return data;
}

WRITE8_MEMBER( sage2_state::ppi1_pc_w )
{
	/*

	    bit     signal

	    PC0     PRES-
	    PC1     U8 SC+
	    PC2     SI+
	    PC3     LEDR+
	    PC4     STROBE-
	    PC5     PRIME-
	    PC6     U38 CL-
	    PC7     RMI-

	*/

	if (!BIT(data, 0))
	{
		// clear parity error interrupt
		m_maincpu->set_input_line(M68K_IRQ_7, CLEAR_LINE);
	}

	// s? interrupt
	m_pic->ir7_w(BIT(data, 2));

	// processor LED
	output_set_led_value(0, BIT(data, 3));

	// centronics
	m_centronics->strobe_w(BIT(data, 4));
	m_centronics->init_prime_w(BIT(data, 5));

	if (!BIT(data, 6))
	{
		// clear ACK interrupt
		m_pic->ir5_w(CLEAR_LINE);
	}

	if (!BIT(data, 7))
	{
		// clear modem interrupt
		m_pic->ir4_w(CLEAR_LINE);
	}
}

static I8255A_INTERFACE( ppi1_intf )
{
	DEVCB_NULL,
	DEVCB_DEVICE_MEMBER(CENTRONICS_TAG, centronics_device, write),
	DEVCB_DRIVER_MEMBER(sage2_state, ppi1_pb_r),
	DEVCB_NULL,
	DEVCB_NULL,
	DEVCB_DRIVER_MEMBER(sage2_state, ppi1_pc_w)
};


//-------------------------------------------------
//  pit8253_config pit0_intf
//-------------------------------------------------

static const struct pit8253_interface pit0_intf =
{
	{
		{
			0, // from U75 OUT0
			DEVCB_LINE_VCC,
			DEVCB_DEVICE_LINE_MEMBER(I8259_TAG, pic8259_device, ir6_w)
		}, {
			XTAL_16MHz/2/125,
			DEVCB_LINE_VCC,
			DEVCB_DEVICE_LINE_MEMBER(I8253_0_TAG, pit8253_device, clk2_w)
		}, {
			0, // from OUT2
			DEVCB_LINE_VCC,
			DEVCB_DEVICE_LINE_MEMBER(I8259_TAG, pic8259_device, ir0_w)
		}
	}
};


//-------------------------------------------------
//  pit8253_config pit1_intf
//-------------------------------------------------

WRITE_LINE_MEMBER( sage2_state::br1_w )
{
	if (state)
	{
		m_usart0->transmit_clock();
		m_usart0->receive_clock();
	}
}

WRITE_LINE_MEMBER( sage2_state::br2_w )
{
	if (state)
	{
		m_usart1->transmit_clock();
		m_usart1->receive_clock();
	}
}

static const struct pit8253_interface pit1_intf =
{
	{
		{
			XTAL_16MHz/2/125,
			DEVCB_LINE_VCC,
			DEVCB_DEVICE_LINE_MEMBER(I8253_0_TAG, pit8253_device, clk0_w)
		}, {
			XTAL_16MHz/2/13,
			DEVCB_LINE_VCC,
			DEVCB_DRIVER_LINE_MEMBER(sage2_state, br1_w)
		}, {
			XTAL_16MHz/2/13,
			DEVCB_LINE_VCC,
			DEVCB_DRIVER_LINE_MEMBER(sage2_state, br2_w)
		}
	}
};


//-------------------------------------------------
//  i8251_interface usart0_intf
//-------------------------------------------------

static const i8251_interface usart0_intf =
{
	DEVCB_DEVICE_LINE_MEMBER(RS232_A_TAG, serial_port_device, tx),
	DEVCB_DEVICE_LINE_MEMBER(RS232_A_TAG, rs232_port_device, dtr_w),
	DEVCB_DEVICE_LINE_MEMBER(RS232_A_TAG, rs232_port_device, rts_w),
	DEVCB_CPU_INPUT_LINE(M68000_TAG, M68K_IRQ_5),
	DEVCB_DEVICE_LINE_MEMBER(I8259_TAG, pic8259_device, ir2_w),
	DEVCB_NULL,
	DEVCB_NULL
};


//-------------------------------------------------
//  i8251_interface usart1_intf
//-------------------------------------------------

static const i8251_interface usart1_intf =
{
	DEVCB_DEVICE_LINE_MEMBER(RS232_B_TAG, serial_port_device, tx),
	DEVCB_DEVICE_LINE_MEMBER(RS232_B_TAG, rs232_port_device, dtr_w),
	DEVCB_DEVICE_LINE_MEMBER(RS232_B_TAG, rs232_port_device, rts_w),
	DEVCB_DEVICE_LINE_MEMBER(I8259_TAG, pic8259_device, ir1_w),
	DEVCB_DEVICE_LINE_MEMBER(I8259_TAG, pic8259_device, ir3_w),
	DEVCB_NULL,
	DEVCB_NULL
};


//-------------------------------------------------
//  upd765_interface fdc_intf
//-------------------------------------------------

static SLOT_INTERFACE_START( sage2_floppies )
	SLOT_INTERFACE( "525qd", FLOPPY_525_QD ) // Mitsubishi M4859
SLOT_INTERFACE_END

void sage2_state::update_fdc_int()
{
	m_maincpu->set_input_line(M68K_IRQ_6, m_fdie && m_fdc_int);
}

void sage2_state::fdc_irq(bool state)
{
	m_fdc_int = state;
	update_fdc_int();
}


//-------------------------------------------------
//  centronics_interface centronics_intf
//-------------------------------------------------

WRITE_LINE_MEMBER( sage2_state::ack_w )
{
	if (!state)
	{
		m_pic->ir5_w(ASSERT_LINE);
	}
}

static const centronics_interface centronics_intf =
{
	DEVCB_DRIVER_LINE_MEMBER(sage2_state, ack_w),
	DEVCB_NULL,
	DEVCB_NULL
};


static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "TERM_FRAME", 0x0f, 0x08 ) // 19200
	DEVICE_INPUT_DEFAULTS( "TERM_FRAME", 0x30, 0x10 ) // 7E1
DEVICE_INPUT_DEFAULTS_END



//**************************************************************************
//  MACHINE INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  MACHINE_START( sage2 )
//-------------------------------------------------

void sage2_state::machine_start()
{
	// find memory regions
	m_rom = memregion(M68000_TAG)->base();

	// setup floppy callbacks
	m_fdc->setup_intrq_cb(upd765a_device::line_cb(FUNC(sage2_state::fdc_irq), this));
}


void sage2_state::machine_reset()
{
	m_reset = 1;
}



//**************************************************************************
//  MACHINE DRIVERS
//**************************************************************************

//-------------------------------------------------
//  MACHINE_CONFIG( sage2 )
//-------------------------------------------------

static MACHINE_CONFIG_START( sage2, sage2_state )
	// basic machine hardware
	MCFG_CPU_ADD(M68000_TAG, M68000, XTAL_16MHz/2)
	MCFG_CPU_PROGRAM_MAP(sage2_mem)

	// devices
	MCFG_PIC8259_ADD(I8259_TAG, INPUTLINE(M68000_TAG, M68K_IRQ_1), VCC, NULL)
	MCFG_I8255A_ADD(I8255A_0_TAG, ppi0_intf)
	MCFG_I8255A_ADD(I8255A_1_TAG, ppi1_intf)
	MCFG_PIT8253_ADD(I8253_0_TAG, pit0_intf)
	MCFG_PIT8253_ADD(I8253_1_TAG, pit1_intf)
	MCFG_I8251_ADD(I8251_0_TAG, usart0_intf)
	MCFG_I8251_ADD(I8251_1_TAG, usart1_intf)
	MCFG_UPD765A_ADD(UPD765_TAG, false, false)
	MCFG_CENTRONICS_PRINTER_ADD(CENTRONICS_TAG, centronics_intf)
	MCFG_FLOPPY_DRIVE_ADD(UPD765_TAG ":0", sage2_floppies, "525qd", floppy_image_device::default_floppy_formats)
	MCFG_FLOPPY_DRIVE_ADD(UPD765_TAG ":1", sage2_floppies, "525qd", floppy_image_device::default_floppy_formats)
	MCFG_IEEE488_BUS_ADD()

	MCFG_RS232_PORT_ADD(RS232_A_TAG, default_rs232_devices, "serial_terminal")
	MCFG_SERIAL_OUT_RX_HANDLER(DEVWRITELINE(I8251_0_TAG, i8251_device, write_rx))
	MCFG_RS232_OUT_DSR_HANDLER(DEVWRITELINE(I8251_0_TAG, i8251_device, write_dsr))
	MCFG_DEVICE_CARD_DEVICE_INPUT_DEFAULTS("serial_terminal", terminal)

	MCFG_RS232_PORT_ADD(RS232_B_TAG, default_rs232_devices, NULL)
	MCFG_SERIAL_OUT_RX_HANDLER(DEVWRITELINE(I8251_1_TAG, i8251_device, write_rx))
	MCFG_RS232_OUT_DSR_HANDLER(DEVWRITELINE(I8251_1_TAG, i8251_device, write_dsr))

	// internal ram
	MCFG_RAM_ADD(RAM_TAG)
	MCFG_RAM_DEFAULT_SIZE("512K")

	// software list
	MCFG_SOFTWARE_LIST_ADD("flop_list", "sage2")
MACHINE_CONFIG_END



//**************************************************************************
//  ROMS
//**************************************************************************

//-------------------------------------------------
//  ROM( sage2 )
//-------------------------------------------------

ROM_START( sage2 )
	ROM_REGION( 0x2000, M68000_TAG, 0 )
	ROM_LOAD16_BYTE( "sage2.u18", 0x0000, 0x1000, CRC(ca9b312d) SHA1(99436a6d166aa5280c3b2d28355c4d20528fe48c) )
	ROM_LOAD16_BYTE( "sage2.u17", 0x0001, 0x1000, CRC(27e25045) SHA1(041cd9d4617473d089f31f18cbb375046c3b61bb) )
ROM_END



//**************************************************************************
//  DRIVER INITIALIZATION
//**************************************************************************

//-------------------------------------------------
//  DRIVER_INIT( sage2 )
//-------------------------------------------------

DIRECT_UPDATE_MEMBER(sage2_state::sage2_direct_update_handler)
{
	if (m_reset && address >= 0xfe0000)
	{
		m_reset = 0;
	}

	return address;
}

DRIVER_INIT_MEMBER(sage2_state,sage2)
{
	address_space &program = machine().device<cpu_device>(M68000_TAG)->space(AS_PROGRAM);
	program.set_direct_update_handler(direct_update_delegate(FUNC(sage2_state::sage2_direct_update_handler), this));
}



//**************************************************************************
//  SYSTEM DRIVERS
//**************************************************************************

//    YEAR  NAME    PARENT  COMPAT  MACHINE INPUT   INIT    COMPANY                             FULLNAME    FLAGS
COMP( 1982, sage2,  0,       0,      sage2,     sage2, sage2_state,    sage2, "Sage Technology", "Sage II", GAME_NOT_WORKING | GAME_NO_SOUND )
