// license:BSD-3-Clause
// copyright-holders:Robbbert
/***************************************************************************

David Griffiths DG640 Video Display Unit.

It was sold by Applied Technology. It uses a MCM6574 as a character generator.
The DG640 also supports blinking, reverse-video, and LORES graphics.
It is a S100 card, originally called "ETI 640" when it was first described
in the April 1978 issue of Electronics Today International (Australia).

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

#include "emu.h"
#include "dg640.h"

#include "emupal.h"
#include "screen.h"



DEFINE_DEVICE_TYPE(S100_DG640, dg640_device, "dg640", "DG640 VDU")

dg640_device::dg640_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, S100_DG640, tag, owner, clock)
	, device_s100_card_interface(mconfig, *this)
	, m_p_chargen(*this, "chargen")
	, m_dsw(*this, "DSW")
{}

void dg640_device::device_start()
{
	m_p_videoram = make_unique_clear<u8[]>(0x400);
	save_pointer(NAME(m_p_videoram), 0x400);
	m_p_attribram = make_unique_clear<u8[]>(0x400);
	save_pointer(NAME(m_p_attribram), 0x400);
}

uint32_t dg640_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
// attributes bit 0 = flash, bit 1 = lores. Also bit 7 of the character = reverse-video (text only).
	uint16_t sy=0,ma=0;
	m_framecnt++;

	for (uint8_t y = 0; y < 16; y++)
	{
		for (uint8_t ra = 0; ra < 16; ra++)
		{
			uint16_t *p = &bitmap.pix(sy++);

			for (uint16_t x = ma; x < ma + 64; x++)
			{
				uint8_t attr = m_p_attribram[x];
				uint8_t chr = m_p_videoram[x];
				bool flash = BIT(m_framecnt, 4) & BIT(attr, 0);

				if (BIT(attr, 1)) // lores gfx - can flash
				{
					if (flash) chr = 0; // blank part of flashing

					uint8_t gfxbit = (ra & 0x0c) >> 1;
					/* Display one line of a lores character (8 pixels) */
					*p++ = BIT(chr, gfxbit);
					*p++ = BIT(chr, gfxbit);
					*p++ = BIT(chr, gfxbit);
					*p++ = BIT(chr, gfxbit);
					gfxbit++;
					*p++ = BIT(chr, gfxbit);
					*p++ = BIT(chr, gfxbit);
					*p++ = BIT(chr, gfxbit);
					*p++ = BIT(chr, gfxbit);
				}
				else
				{
					uint8_t gfx = 0;

					if (!flash)
					{
						uint8_t inv = BIT(chr, 7) ? 0xff : 0; // text with bit 7 high is reversed
						chr &= 0x7f;
						gfx = inv;

						// if g,j,p,q,y; lower the descender
						if ((chr==0x2c)||(chr==0x3b)||(chr==0x67)||(chr==0x6a)||(chr==0x70)||(chr==0x71)||(chr==0x79))
						{
							if (ra > 6)
								gfx = m_p_chargen[(chr<<4) | (ra-7) ] ^ inv;
						}
						else
						{
							if ((ra > 3) & (ra < 13))
								gfx = m_p_chargen[(chr<<4) | (ra-4) ] ^ inv;
						}
					}

					/* Display a scanline of a character */
					*p++ = BIT(gfx, 7);
					*p++ = BIT(gfx, 6);
					*p++ = BIT(gfx, 5);
					*p++ = BIT(gfx, 4);
					*p++ = BIT(gfx, 3);
					*p++ = BIT(gfx, 2);
					*p++ = BIT(gfx, 1);
					*p++ = BIT(gfx, 0);
				}
			}
		}
		ma+=64;
	}
	return 0;
}

u8 dg640_device::s100_smemr_r(offs_t offset)
{
	if (m_dsw->read() != (offset & 0xf800) >> 11)
		return 0xff;

	if ((offset & 0x400) == 0)
		return m_p_videoram[offset & 0x3ff];
	else
		return m_p_attribram[offset & 0x3ff];
}

void dg640_device::s100_mwrt_w(offs_t offset, u8 data)
{
	if (m_dsw->read() != (offset & 0xf800) >> 11)
		return;

	if ((offset & 0x400) == 0)
		m_p_videoram[offset & 0x3ff] = data;
	else
		m_p_attribram[offset & 0x3ff] = data;
}


static INPUT_PORTS_START(dg640)
	PORT_START("DSW")
	PORT_DIPNAME(0x1f, 0x0f, "Base Address") PORT_DIPLOCATION("SW1:1,2,3,4,5")
	PORT_DIPSETTING(0x00, "0000")
	PORT_DIPSETTING(0x01, "0800")
	PORT_DIPSETTING(0x02, "1000")
	PORT_DIPSETTING(0x03, "1800")
	PORT_DIPSETTING(0x04, "2000")
	PORT_DIPSETTING(0x05, "2800")
	PORT_DIPSETTING(0x06, "3000")
	PORT_DIPSETTING(0x07, "3800")
	PORT_DIPSETTING(0x08, "4000")
	PORT_DIPSETTING(0x09, "4800")
	PORT_DIPSETTING(0x0a, "5000")
	PORT_DIPSETTING(0x0b, "5800")
	PORT_DIPSETTING(0x0c, "6000")
	PORT_DIPSETTING(0x0d, "6800")
	PORT_DIPSETTING(0x0e, "7000")
	PORT_DIPSETTING(0x0f, "7800")
	PORT_DIPSETTING(0x10, "8000")
	PORT_DIPSETTING(0x11, "8800")
	PORT_DIPSETTING(0x12, "9000")
	PORT_DIPSETTING(0x13, "9800")
	PORT_DIPSETTING(0x14, "A000")
	PORT_DIPSETTING(0x15, "A800")
	PORT_DIPSETTING(0x16, "B000")
	PORT_DIPSETTING(0x17, "B800")
	PORT_DIPSETTING(0x18, "C000")
	PORT_DIPSETTING(0x19, "C800")
	PORT_DIPSETTING(0x1a, "D000")
	PORT_DIPSETTING(0x1b, "D800")
	PORT_DIPSETTING(0x1c, "E000")
	PORT_DIPSETTING(0x1d, "E800")
	PORT_DIPSETTING(0x1e, "F000")
	PORT_DIPSETTING(0x1f, "F800")
INPUT_PORTS_END

ioport_constructor dg640_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(dg640);
}


/* F4 Character Displayer */
static const gfx_layout dg640_charlayout =
{
	7, 9,                   /* 7 x 9 characters */
	128,                  /* 128 characters */
	1,                  /* 1 bits per pixel */
	{ 0 },                  /* no bitplanes */
	/* x offsets */
	{ 0, 1, 2, 3, 4, 5, 6 },
	/* y offsets */
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 },
	8*16                    /* every char takes 16 bytes */
};

static GFXDECODE_START( gfx_dg640 )
	GFXDECODE_ENTRY( "chargen", 0x0000, dg640_charlayout, 0, 1 )
GFXDECODE_END

void dg640_device::device_add_mconfig(machine_config &config)
{
	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
	screen.set_color(rgb_t::amber());
	screen.set_raw(12_MHz_XTAL, 768, 0, 512, 312, 0, 256); // 15625 Hz horizontal
	screen.set_screen_update(FUNC(dg640_device::screen_update));
	screen.set_palette("palette");

	GFXDECODE(config, "gfxdecode", "palette", gfx_dg640);
	PALETTE(config, "palette", palette_device::MONOCHROME);
}

ROM_START(dg640)
	ROM_REGION( 0x0800, "chargen", 0 )
	ROM_LOAD( "6574.bin", 0x0000, 0x0800, CRC(fd75df4f) SHA1(4d09aae2f933478532b7d3d1a2dee7123d9828ca) )
	ROM_FILL(0, 16, 0x00)
ROM_END

const tiny_rom_entry *dg640_device::device_rom_region() const
{
	return ROM_NAME(dg640);
}
