/*****************************************************************************
 *
 * video/dl1416.c
 *
 * DL1416
 *
 * 4-Digit 16-Segment Alphanumeric Intelligent Display
 * with Memory/Decoder/Driver
 *
 * Notes:
 *   - Currently supports the DL1416T.
 *   - Partial support for DL1416B is available, it just needs the right
 *     character set and MAME core support for its display.
 *   - Cursor support is implemented but not tested, as the AIM65 does not
 *     seem to use it.
 *
 * Todo:
 *   - Is the DL1416A identical to the DL1416T? If not, we need to add
 *     support for it.
 *
 * Changes:
 *   - 2007-07-30: Initial version.  [Dirk Best]
 *
 * **************************************************************************/


/*****************************************************************************
 Includes
*****************************************************************************/


#include "dl1416.h"



/*****************************************************************************
 Macros
*****************************************************************************/


#define MAX_DL1416 (5)

#define SEG_UNDEF  (-1)
#define SEG_BLANK  (0)
#define SEG_CURSOR (0xffff)



/*****************************************************************************
 Type definitions
*****************************************************************************/

/* DL1416 chip state */
typedef struct _dl1416_state dl1416_state;

struct _dl1416_state
{
	const dl1416_interface *intf;

	int write_enable;
	int chip_enable;
	int cursor_enable;

	UINT16 cursor_ram[4];
};



/*****************************************************************************
 Global variables
*****************************************************************************/


/* We use the following order for the segments:
 *
 *   000 111
 *  7D  A  E2
 *  7 D A E 2
 *  7  DAE  2
 *   888 999
 *  6  CBF  3
 *  6 C B F 3
 *  6C  B  F3
 *   555 444
 */

/* Character set DL1416T */
static const int dl1416t_segments[128] = {
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	0x0000,    0x2421,    0x0480,    0x0f3c,    /*   ! " # */
	0x0fbb,    0x5f99,    0xa579,    0x4000,    /* $ % & ' */
	0xc000,    0x3000,    0xff00,    0x0f00,    /* ( ) * + */
	0x1000,    0x0300,    0x0020,    0x5000,    /* , - . / */
	0x0ce1,    0x0c00,    0x0561,    0x0d21,    /* 0 1 2 3 */
	0x0d80,    0x09a1,    0x09e1,    0x0c01,    /* 4 5 6 7 */
	0x0de1,    0x0da1,    0x0021,    0x1001,    /* 8 9 : ; */
	0x5030,    0x0330,    0xa030,    0x0a07,    /* < = > ? */
	0x097f,    0x03cf,    0x0e3f,    0x00f3,    /* @ A B C */
	0x0c3f,    0x01f3,    0x01c3,    0x02fb,    /* D E F G */
	0x03cc,    0x0c33,    0x0c63,    0xc1c0,    /* H I J K */
	0x00f0,    0x60cc,    0xa0cc,    0x00ff,    /* L M N O */
	0x03c7,    0x80ff,    0x83c7,    0x03bb,    /* P Q R S */
	0x0c03,    0x00fc,    0x50c0,    0x90cc,    /* T U V W */
	0xf000,    0x6800,    0x5033,    0x00e1,    /* X Y Z [ */
	0xa000,    0x001e,    0x9000,    0x0030,    /* \ ] ^ _ */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, /* undefined */
	SEG_UNDEF, SEG_UNDEF, SEG_UNDEF, SEG_UNDEF  /* undefined */
};


static dl1416_state dl1416[MAX_DL1416];



/*****************************************************************************
 Implementation
*****************************************************************************/

/* Config */
void dl1416_config(int which, const dl1416_interface *intf)
{
	assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call dl1416_config at init time!");
	assert_always(which < MAX_DL1416, "'which' exceeds maximum number of configured DL1416s!");

	dl1416[which].intf = intf;

	state_save_register_item("dl1416", which, dl1416[which].chip_enable);
	state_save_register_item("dl1416", which, dl1416[which].cursor_enable);
	state_save_register_item("dl1416", which, dl1416[which].write_enable);
	state_save_register_item_array("dl1416", which, dl1416[which].cursor_ram);
}


/* Reset */
void dl1416_reset(int which)
{
	int i;

	assert_always(which < MAX_DL1416, "'which' exceeds maximum number of configured DL1416s!");

	/* Disable all lines */
	dl1416[which].chip_enable = FALSE;
	dl1416[which].write_enable = FALSE;
	dl1416[which].cursor_enable = FALSE;

	/* Randomize cursor memory */
	for (i = 0; i < 4; i++)
		dl1416[which].cursor_ram[i] = mame_rand(Machine);
}


/* Write enable, active low */
void dl1416_set_input_w(int which, int data)
{
	assert_always(which < MAX_DL1416, "'which' exceeds maximum number of configured DL1416s!");
	dl1416[which].write_enable = !data;
}


/* Chip enable, active low */
void dl1416_set_input_ce(int which, int data)
{
	assert_always(which < MAX_DL1416, "'which' exceeds maximum number of configured DL1416s!");
	dl1416[which].chip_enable = !data;
}


/* Cursor enable, active low */
void dl1416_set_input_cu(int which, int data)
{
	assert_always(which < MAX_DL1416, "'which' exceeds maximum number of configured DL1416s!");
	dl1416[which].cursor_enable = !data;
}


/* Data */
void dl1416_write(int which, offs_t offset, UINT8 data)
{
	int i, digit;

	assert_always(which < MAX_DL1416, "'which' exceeds maximum number of configured DL1416s!");

	offset &= 0x03; /* A0-A1 */
	data &= 0x7f;   /* D0-D6 */

	/* Only try to update the data if we are enabled and write is enabled */
	if (dl1416[which].chip_enable && dl1416[which].write_enable)
	{
		if (dl1416[which].cursor_enable)
		{
			switch (dl1416[which].intf->type)
			{

			case DL1416B:

				/* The cursor will be set if D0 is high and the original */
				/* character restored otherwise */
				digit = data & 1 ? SEG_CURSOR : dl1416[which].cursor_ram[offset];

				/* Call update function */
				dl1416[which].intf->digit_changed(offset, digit);

				break;

			case DL1416T:

				for (i = 0; i < 4; i++)
				{
					/* Save old digit */
					int previous_digit = dl1416[which].cursor_ram[i];

					/* Either set the cursor or restore the original character */
					digit = data & i ? SEG_CURSOR : dl1416[which].cursor_ram[i];

					/* Call update function if we changed something */
					if (previous_digit != digit)
						dl1416[which].intf->digit_changed(i, digit);
				}
				break;

			}
		}
		else
		{
			/* On the DL1416T, a digit can only be changed if there is no */
			/* previously stored cursor, or overriden by an undefined */
			/* character (blank) */
			if ((dl1416[which].intf->type != DL1416T) || (
				(dl1416[which].cursor_ram[offset] != SEG_CURSOR) ||
				(dl1416t_segments[data] == SEG_UNDEF)))
			{
				/* Load data */
				digit = dl1416t_segments[data];

				/* Undefined characters are replaced by blanks */
				if (digit == SEG_UNDEF)
					digit = SEG_BLANK;

				/* Save value */
				dl1416[which].cursor_ram[offset] = digit;

				/* Call update function */
				dl1416[which].intf->digit_changed(offset, digit);
			}
		}
	}
}


/* Standard handlers */
WRITE8_HANDLER( dl1416_0_w ) { dl1416_write(0, offset, data); }
WRITE8_HANDLER( dl1416_1_w ) { dl1416_write(1, offset, data); }
WRITE8_HANDLER( dl1416_2_w ) { dl1416_write(2, offset, data); }
WRITE8_HANDLER( dl1416_3_w ) { dl1416_write(3, offset, data); }
WRITE8_HANDLER( dl1416_4_w ) { dl1416_write(4, offset, data); }
