// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************
HD44102 Dot Matrix Liquid Crystal Graphic Display Column Driver emulation
**********************************************************************/
#include "emu.h"
#include "hd44102.h"
//#define VERBOSE 1
#include "logmacro.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
#define CONTROL_DISPLAY_OFF 0x38
#define CONTROL_DISPLAY_ON 0x39
#define CONTROL_COUNT_DOWN_MODE 0x3a
#define CONTROL_COUNT_UP_MODE 0x3b
#define CONTROL_Y_ADDRESS_MASK 0x3f
#define CONTROL_X_ADDRESS_MASK 0xc0
#define CONTROL_DISPLAY_START_PAGE 0x3e
#define STATUS_BUSY 0x80 /* not supported */
#define STATUS_COUNT_UP 0x40
#define STATUS_DISPLAY_OFF 0x20
#define STATUS_RESET 0x10 /* not supported */
// device type definition
DEFINE_DEVICE_TYPE(HD44102, hd44102_device, "hd44102", "Hitachi HD44102 LCD Controller")
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
//-------------------------------------------------
// count_up_or_down -
//-------------------------------------------------
inline void hd44102_device::count_up_or_down()
{
if (m_status & STATUS_COUNT_UP)
{
if (++m_y > 49) m_y = 0;
}
else
{
if (--m_y < 0) m_y = 49;
}
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// hd44102_device - constructor
//-------------------------------------------------
hd44102_device::hd44102_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, HD44102, tag, owner, clock),
device_video_interface(mconfig, *this),
m_cs2(0),
m_page(0),
m_x(0),
m_y(0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void hd44102_device::device_start()
{
// register for state saving
save_item(NAME(m_ram[0]));
save_item(NAME(m_ram[1]));
save_item(NAME(m_ram[2]));
save_item(NAME(m_ram[3]));
save_item(NAME(m_status));
save_item(NAME(m_output));
save_item(NAME(m_cs2));
save_item(NAME(m_page));
save_item(NAME(m_x));
save_item(NAME(m_y));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void hd44102_device::device_reset()
{
m_status = STATUS_DISPLAY_OFF | STATUS_COUNT_UP;
}
//-------------------------------------------------
// read - register read
//-------------------------------------------------
READ8_MEMBER( hd44102_device::read )
{
uint8_t data = 0;
if (m_cs2)
{
data = (offset & 0x01) ? data_r(space, offset) : status_r(space, offset);
}
return data;
}
//-------------------------------------------------
// write - register write
//-------------------------------------------------
WRITE8_MEMBER( hd44102_device::write )
{
if (m_cs2)
{
(offset & 0x01) ? data_w(space, offset, data) : control_w(space, offset, data);
}
}
//-------------------------------------------------
// status_r - status read
//-------------------------------------------------
READ8_MEMBER( hd44102_device::status_r )
{
return m_status;
}
//-------------------------------------------------
// control_w - control write
//-------------------------------------------------
WRITE8_MEMBER( hd44102_device::control_w )
{
if (m_status & STATUS_BUSY) return;
switch (data)
{
case CONTROL_DISPLAY_OFF:
LOG("HD44102 Display Off\n");
m_status |= STATUS_DISPLAY_OFF;
break;
case CONTROL_DISPLAY_ON:
LOG("HD44102 Display On\n");
m_status &= ~STATUS_DISPLAY_OFF;
break;
case CONTROL_COUNT_DOWN_MODE:
LOG("HD44102 Count Down Mode\n");
m_status &= ~STATUS_COUNT_UP;
break;
case CONTROL_COUNT_UP_MODE:
LOG("HD44102 Count Up Mode\n");
m_status |= STATUS_COUNT_UP;
break;
default:
{
const int x = (data & CONTROL_X_ADDRESS_MASK) >> 6;
const int y = data & CONTROL_Y_ADDRESS_MASK;
if ((data & CONTROL_Y_ADDRESS_MASK) == CONTROL_DISPLAY_START_PAGE)
{
LOG("HD44102 Display Start Page %u\n", x);
m_page = x;
}
else if (y > 49)
{
logerror("HD44102 Invalid Address X %u Y %u (%02x)!\n", data, x, y);
}
else
{
LOG("HD44102 Address X %u Y %u (%02x)\n", data, x, y);
m_x = x;
m_y = y;
}
}
}
}
//-------------------------------------------------
// data_r - data read
//-------------------------------------------------
READ8_MEMBER( hd44102_device::data_r )
{
uint8_t data = m_output;
m_output = m_ram[m_x][m_y];
count_up_or_down();
return data;
}
//-------------------------------------------------
// data_w - data write
//-------------------------------------------------
WRITE8_MEMBER( hd44102_device::data_w )
{
m_ram[m_x][m_y] = data;
count_up_or_down();
}
//-------------------------------------------------
// cs2_w - chip select 2 write
//-------------------------------------------------
WRITE_LINE_MEMBER( hd44102_device::cs2_w )
{
m_cs2 = state;
}
//-------------------------------------------------
// update_screen - update screen
//-------------------------------------------------
uint32_t hd44102_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
for (int y = 0; y < 50; y++)
{
int z = m_page << 3;
for (int x = 0; x < 32; x++)
{
uint8_t data = m_ram[z / 8][y];
int sy = m_sy + z;
int sx = m_sx + y;
if (cliprect.contains(sx, sy))
{
int color = (m_status & STATUS_DISPLAY_OFF) ? 0 : BIT(data, z % 8);
bitmap.pix16(sy, sx) = color;
}
z++;
z %= 32;
}
}
return 0;
}