// license:BSD-3-Clause
// copyright-holders:Tim Schuerewegen
/*
Hitachi HD66421 LCD Controller/Driver
(c) 2001-2007 Tim Schuerewegen
*/
#include "emu.h"
#include "hd66421.h"
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
//#define HD66421_BRIGHTNESS_DOES_NOT_WORK
#define LOG_LEVEL 1
#define _logerror(level,x) do { if (LOG_LEVEL > level) logerror x; } while (0)
#define HD66421_RAM_SIZE (hd66421_device::WIDTH * hd66421_device::HEIGHT / 4) // 2-bits per pixel
// R0 - control register 1
#define LCD_R0_RMW 0x80 // read-modify-write mode
#define LCD_R0_DISP 0x40 // display on/off
#define LCD_R0_STBY 0x20 // standby (internal operation and power circuit halt)
#define LCD_R0_PWR 0x10
#define LCD_R0_AMP 0x08
#define LCD_R0_REV 0x04 // reverse
#define LCD_R0_HOLT 0x02
#define LCD_R0_ADC 0x01
// R1 - control register 2
#define LCD_R1_BIS1 0x80 // bias ratio (bit 1)
#define LCD_R1_BIS0 0x40 // bias ratio (bit 0)
#define LCD_R1_WLS 0x20
#define LCD_R1_GRAY 0x10 // grayscale palette 4/32
#define LCD_R1_DTY1 0x08 // display duty cycle (bit 1)
#define LCD_R1_DTY0 0x04 // display duty cycle (bit 0)
#define LCD_R1_INC 0x02
#define LCD_R1_BLK 0x01 // blink function
// register 0 to 16
#define LCD_REG_CONTROL_1 0x00 // control register 1
#define LCD_REG_CONTROL_2 0x01 // control register 2
#define LCD_REG_ADDR_X 0x02 // x address register
#define LCD_REG_ADDR_Y 0x03 // y address register
#define LCD_REG_RAM 0x04 // display ram access register
#define LCD_REG_START_Y 0x05 // display start line register
#define LCD_REG_BLINK_START 0x06 // blink start line register
#define LCD_REG_BLINK_END 0x07 // blink end line register
#define LCD_REG_BLINK_1 0x08 // blink register 1
#define LCD_REG_BLINK_2 0x09 // blink register 2
#define LCD_REG_BLINK_3 0x0A // blink register 3
#define LCD_REG_PARTIAL 0x0B // partial display block register
#define LCD_REG_COLOR_1 0x0C // gray scale palette 1 (0,0)
#define LCD_REG_COLOR_2 0x0D // gray scale palette 2 (0,1)
#define LCD_REG_COLOR_3 0x0E // gray scale palette 3 (1,0)
#define LCD_REG_COLOR_4 0x0F // gray scale palette 4 (1,1)
#define LCD_REG_CONTRAST 0x10 // contrast control register
#define LCD_REG_PLANE 0x11 // plane selection register
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// devices
DEFINE_DEVICE_TYPE(HD66421, hd66421_device, "hd66421", "Hitachi HD66421 LCD Controller")
// default address map
void hd66421_device::hd66421(address_map &map)
{
if (!has_configured_map(0))
map(0x0000, HD66421_RAM_SIZE).ram();
}
//-------------------------------------------------
// memory_space_config - return a description of
// any address spaces owned by this device
//-------------------------------------------------
device_memory_interface::space_config_vector hd66421_device::memory_space_config() const
{
return space_config_vector {
std::make_pair(0, &m_space_config)
};
}
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
//-------------------------------------------------
// readbyte - read a byte at the given address
//-------------------------------------------------
inline uint8_t hd66421_device::readbyte(offs_t address)
{
return space().read_byte(address);
}
//-------------------------------------------------
// writebyte - write a byte at the given address
//-------------------------------------------------
inline void hd66421_device::writebyte(offs_t address, uint8_t data)
{
space().write_byte(address, data);
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// hd66421_device - constructor
//-------------------------------------------------
hd66421_device::hd66421_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, HD66421, tag, owner, clock)
, device_memory_interface(mconfig, *this)
, m_space_config("videoram", ENDIANNESS_LITTLE, 8, 17, 0, address_map_constructor(FUNC(hd66421_device::hd66421), this))
, m_cmd(0)
, m_x(0)
, m_y(0)
, m_palette(*this, "palette")
{
for (auto &elem : m_reg)
elem = 0;
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void hd66421_device::device_start()
{
// register for state saving
save_item(NAME(m_cmd));
save_item(NAME(m_reg));
save_item(NAME(m_x));
save_item(NAME(m_y));
}
READ8_MEMBER( hd66421_device::reg_idx_r )
{
_logerror( 2, ("reg_idx_r\n"));
return m_cmd;
}
WRITE8_MEMBER( hd66421_device::reg_idx_w )
{
_logerror( 2, ("reg_idx_w (%02X)\n", data));
m_cmd = data;
}
READ8_MEMBER( hd66421_device::reg_dat_r )
{
_logerror( 2, ("reg_dat_r\n"));
return m_reg[m_cmd];
}
WRITE8_MEMBER( hd66421_device::reg_dat_w )
{
_logerror( 2, ("reg_dat_w (%02X)\n", data));
m_reg[m_cmd] = data;
switch (m_cmd)
{
case LCD_REG_ADDR_X :
m_x = data;
break;
case LCD_REG_ADDR_Y :
m_y = data;
break;
case LCD_REG_RAM :
{
uint8_t r1;
writebyte(m_y * (WIDTH / 4) + m_x, data);
r1 = m_reg[LCD_REG_CONTROL_2];
if (r1 & 0x02)
m_x++;
else
m_y++;
if (m_x >= (WIDTH / 4))
{
m_x = 0;
m_y++;
}
if (m_y >= HEIGHT)
m_y = 0;
}
break;
}
}
void hd66421_device::plot_pixel(bitmap_ind16 &bitmap, int x, int y, uint32_t color)
{
bitmap.pix16(y, x) = (uint16_t)color;
}
uint32_t hd66421_device::update_screen(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
pen_t pen[4];
_logerror( 1, ("video_update_hd66421\n"));
// update palette
for (int i = 0; i < 4; i++)
{
double bright;
int temp;
temp = 31 - (m_reg[LCD_REG_COLOR_1 + i] - m_reg[LCD_REG_CONTRAST] + 0x03);
if (temp < 0) temp = 0;
if (temp > 31) temp = 31;
bright = 1.0 * temp / 31;
pen[i] = i;
#ifdef HD66421_BRIGHTNESS_DOES_NOT_WORK
m_palette->set_pen_color(pen[i], 255 * bright, 255 * bright, 255 * bright);
#else
m_palette->set_pen_contrast(pen[i], bright);
#endif
}
// draw bitmap (bottom to top)
if (m_reg[0] & LCD_R0_DISP)
{
int x, y;
x = 0;
y = HEIGHT - 1;
for (int i = 0; i < HD66421_RAM_SIZE; i++)
{
plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 6) & 3]);
plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 4) & 3]);
plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 2) & 3]);
plot_pixel(bitmap, x++, y, pen[(readbyte(i) >> 0) & 3]);
if (x >= WIDTH)
{
x = 0;
y = y - 1;
}
}
}
else
{
rectangle rect(0, WIDTH - 1, 0, HEIGHT - 1);
bitmap.fill(m_palette->white_pen(), rect);
}
return 0;
}
void hd66421_device::hd66421_palette(palette_device &palette) const
{
// init palette
for (int i = 0; i < 4; i++)
{
palette.set_pen_color(i, rgb_t::white());
#ifndef HD66421_BRIGHTNESS_DOES_NOT_WORK
palette.set_pen_contrast(i, double(i) / (4 - 1));
#endif
}
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void hd66421_device::device_add_mconfig(machine_config &config)
{
PALETTE(config, m_palette, FUNC(hd66421_device::hd66421_palette), 4);
}