summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/lc7985.cpp
blob: ef01cbb6a2cfd2951fc5e7973d00223235ceb38c (plain) (tree)
































                                                                                                                                                          
                                                                               

































                                                                         
                                                     




































































                                              
                                              



                                  
                                              










                                             
                                              



                                 
                                              



                                 
                                              





                                      
                                                 
































































































































                                                                                                
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
/***************************************************************************

        Sanyo LC7985NA/LC7985ND LCD controller

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

#include "emu.h"
#include "lc7985.h"

DEFINE_DEVICE_TYPE(LC7985, lc7985_device, "lc7985", "Sanyo LC7985NA/LC7985ND LCD controller")


ROM_START( lc7985 )
  ROM_REGION( 0x1000, "cgrom", 0 )
  ROM_LOAD( "lc7985.bin", 0x0000, 0x1000,  BAD_DUMP CRC(fdc64160) SHA1(8e6b54f8fb7c4c15aab2e65dd1a44729b97423b1)) // from page 12 of the LC7985D datasheet
ROM_END

lc7985_device::lc7985_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, LC7985, tag, owner, clock),
	  m_cgrom_region(*this, DEVICE_SELF)
{
}

const tiny_rom_entry *lc7985_device::device_rom_region() const
{
	return ROM_NAME(lc7985);
}

void lc7985_device::device_start()
{
	m_cgrom = m_cgrom_region.found() ? m_cgrom_region : memregion("cgrom")->base();
	m_busy_timer = timer_alloc(FUNC(lc7985_device::clear_busy_flag), this);

	save_item(NAME(m_ddram));
	save_item(NAME(m_cgram));
	save_item(NAME(m_busy_flag));
	save_item(NAME(m_ddac));
	save_item(NAME(m_cgac));
	save_item(NAME(m_shift));
	save_item(NAME(m_access_ddram));
	save_item(NAME(m_function));
	save_item(NAME(m_cds));
}

void lc7985_device::device_reset()
{
	memset(m_ddram, 0x20, sizeof(m_ddram)); // filled with SPACE char
	memset(m_cgram, 0, sizeof(m_cgram));
	m_ddac = 0x00;
	m_cgac = 0x00;
	m_shift = 0x00;
	m_access_ddram = false;
	m_function = 0x00;
	m_cds = 0x00;
	m_display = 0x00;
	m_entry = 0x02;

	busy(attotime::from_msec(10));
}

void lc7985_device::busy(attotime time)
{
	m_busy_flag = true;
	m_busy_timer->adjust(time);
}

TIMER_CALLBACK_MEMBER(lc7985_device::clear_busy_flag)
{
	m_busy_flag = false;
}

void lc7985_device::inc_ddac()
{
	if(m_function & 0x08) { // 2 lines
		if(m_ddac == 39)
			m_ddac = 64;
		else if(m_ddac == 64+39)
			m_ddac = 0;
		else
			m_ddac++;
	} else {
		if(m_ddac == 79)
			m_ddac = 0;
		else
			m_ddac++;
	}
}

void lc7985_device::dec_ddac()
{
	if(m_function & 0x08) { // 2 lines
		if(m_ddac == 64)
			m_ddac = 39;
		else if(m_ddac == 0)
			m_ddac = 64+39;
		else
			m_ddac--;
	} else {
		if(m_ddac == 0)
			m_ddac = 79;
		else
			m_ddac--;
	}
}

void lc7985_device::shift_left()
{
	if(m_shift == 79)
		m_shift = 0;
	else
		m_shift++;
}

void lc7985_device::shift_right()
{
	if(m_shift == 0)
		m_shift = 79;
	else
		m_shift--;
}

void lc7985_device::ir_w(u8 data)
{
	if(m_busy_flag)
		return;

	if(data & 0x80) {
		// Set DDRAM address
		m_ddac = data & 0x7f;
		m_access_ddram = true;
		busy(attotime::from_usec(40));

	} else if(data & 0x40) {
		// Set CGRAM address
		m_cgac = data & 0x3f;
		m_access_ddram = false;
		busy(attotime::from_usec(40));

	} else if(data & 0x20) {
		// Set Function
		m_function = data;
		busy(attotime::from_usec(40));

	} else if(data & 0x10) {
		// Cursor/Display Shift
		m_access_ddram = true;
		switch((data >> 2) & 3) {
		case 0: dec_ddac(); break;
		case 1: inc_ddac(); break;
		case 2: shift_left(); break;
		case 3: shift_right(); break;
		}

		busy(attotime::from_usec(40));

	} else if(data & 0x08) {
		// Display On/Off
		m_display = data;
		busy(attotime::from_usec(40));

	} else if(data & 0x04) {
		// Set Entry Mode
		m_entry = data;
		busy(attotime::from_usec(40));

	} else if(data & 0x02) {
		// Cursor home
		m_ddac = 0;
		m_shift = 0;
		m_access_ddram = true;
		busy(attotime::from_usec(16400));

	} else if(data & 0x01) {
		// Display clear
		memset(m_ddram, 0x20, sizeof(m_ddram));
		m_ddac = 0x00;
		m_entry |= 0x02;
		busy(attotime::from_usec(16400));
	}
}

u8 lc7985_device::status_r()
{
	return (m_access_ddram ? m_ddac : m_cgac) | (m_busy_flag ? 0x80 : 0x00);
}

void lc7985_device::dr_w(u8 data)
{
	if(m_access_ddram) {
		m_ddram[(m_function & 0x08) && m_ddac >= 64 ? m_ddac - (64-40) : m_ddac] = data;
		switch(m_entry & 0x03) {
		case 0: dec_ddac(); break;
		case 1: dec_ddac(); shift_right(); break;
		case 2: inc_ddac(); break;
		case 3: inc_ddac(); shift_left(); break;
		}

	} else {
		m_cgram[m_cgac] = data;
		if(m_entry & 0x02)
			m_cgac = (m_cgac + 1) & 0x3f;
		else
			m_cgac = (m_cgac - 1) & 0x3f;
	}
}

u8 lc7985_device::dr_r()
{
	u8 res;
	if(m_access_ddram) {
		res = m_ddram[(m_function & 0x08) && m_ddac >= 64 ? m_ddac - (64-40) : m_ddac];
		if(m_entry & 0x02)
			inc_ddac();
		else
			dec_ddac();

	} else {
		res = m_cgram[m_cgac];
		if(m_entry & 0x02)
			m_cgac = (m_cgac + 1) & 0x3f;
		else
			m_cgac = (m_cgac - 1) & 0x3f;
	}
	return res;
}

const u8 *lc7985_device::render()
{
	memset(m_render_buffer, 0, sizeof(m_render_buffer));
	if(!(m_display & 0x04))
		return m_render_buffer;

	if(m_function & 0x08) {
		for(int y = 0; y != 2; y++) {
			for(int x = 0; x != 40; x ++) {
				u8 c = m_ddram[((x + 80 - m_shift) % 40) + 40*y];
				const u8 *src = c < 32 ? m_cgram + 8*(c & 7) : m_cgrom + 16 * c;
				u8 *dest = m_render_buffer + 8 * y + 16 * x;
				for(int z = 0; z != 8; z ++)
					*dest++ = *src++ & 0x1f;
			}
		}
		if(m_display & 0x03) {
			int cx = ((m_ddac & 0x3f) + 80 - m_shift) % 80;
			u8 *dest = m_render_buffer + (m_ddac >= 0x40 ? 8 : 0) + 16*cx;
			if(m_display & 0x02)
				dest[7] = 0x1f;
			if(m_display & 0x01) {
				bool on = int(machine().time().as_double() / 0.409) & 1;
				if(on)
					for(int z = 0; z != 8; z ++)
						*dest++ = 0x1f;
			}
		}

	} else if(m_function & 0x04) {
		for(int x = 0; x != 80; x ++) {
			u8 c = m_ddram[(x + 80 - m_shift) % 80];
			const u8 *src = c < 32 ? m_cgram + 8*(c & 6) : m_cgrom + 16 * c;
			u8 *dest = m_render_buffer + 16 * x;
			for(int z = 0; z != 11; z ++)
				*dest++ = *src++ & 0x1f;
		}
		if(m_display & 0x03) {
			int cx = (m_ddac + 80 - m_shift) % 80;
			u8 *dest = m_render_buffer + 16*cx;
			if(m_display & 0x02)
				dest[10] = 0x1f;
			if(m_display & 0x01) {
				bool on = int(machine().time().as_double() / 0.409) & 1;
				if(on)
					for(int z = 0; z != 11; z ++)
						*dest++ = 0x1f;
			}
		}

	} else {
		for(int x = 0; x != 80; x ++) {
			u8 c = m_ddram[(x + 80 - m_shift) % 80];
			const u8 *src = c < 32 ? m_cgram + 8*(c & 7) : m_cgrom + 16 * c;
			u8 *dest = m_render_buffer + 16 * x;
			for(int z = 0; z != 8; z ++)
				*dest++ = *src++ & 0x1f;
		}
		if(m_display & 0x03) {
			int cx = (m_ddac + 80 - m_shift) % 80;
			u8 *dest = m_render_buffer + 16*cx;
			if(m_display & 0x02)
				dest[7] = 0x1f;
			if(m_display & 0x01) {
				bool on = int(machine().time().as_double() / 0.409) & 1;
				if(on)
					for(int z = 0; z != 8; z ++)
						*dest++ = 0x1f;
			}
		}
	}
	return m_render_buffer;
}