// license:BSD-3-Clause // copyright-holders:Curt Coder /********************************************************************** HD44102 Dot Matrix Liquid Crystal Graphic Display Column Driver Not to be confused with HD44100. Includes 4*50*8bit RAM. TODO: - properly emulate CS pins if needed? (there's 3, for enabling read and/or write) - add BS pin when needed (4-bit mode) - busy flag - reset state - what happens if Y address is invalid? (set to > 49) **********************************************************************/ #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") //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // hd44102_device - constructor //------------------------------------------------- hd44102_device::hd44102_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, HD44102, tag, owner, clock), m_sx(0), m_sy(0) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void hd44102_device::device_start() { // zerofill m_status = 0; m_output = 0; m_page = 0; m_x = 0; m_y = 0; memset(m_ram, 0, sizeof(m_ram)); memset(m_render_buf, 0, sizeof(m_render_buf)); // register for state saving save_item(NAME(m_ram)); save_item(NAME(m_render_buf)); save_item(NAME(m_status)); save_item(NAME(m_output)); save_item(NAME(m_page)); save_item(NAME(m_x)); save_item(NAME(m_y)); save_item(NAME(m_sx)); save_item(NAME(m_sy)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void hd44102_device::device_reset() { m_status = STATUS_DISPLAY_OFF | STATUS_COUNT_UP; } //------------------------------------------------- // read - register read //------------------------------------------------- u8 hd44102_device::read(offs_t offset) { return (offset & 0x01) ? data_r() : status_r(); } //------------------------------------------------- // write - register write //------------------------------------------------- void hd44102_device::write(offs_t offset, u8 data) { (offset & 0x01) ? data_w(data) : control_w(data); } //------------------------------------------------- // status_r - status read //------------------------------------------------- u8 hd44102_device::status_r() { return m_status; } //------------------------------------------------- // control_w - control write //------------------------------------------------- void hd44102_device::control_w(u8 data) { 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; } } } } //------------------------------------------------- // count_up_or_down - //------------------------------------------------- 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; } } //------------------------------------------------- // data_r - data read //------------------------------------------------- u8 hd44102_device::data_r() { u8 data = m_output; m_output = m_ram[m_x][m_y]; count_up_or_down(); return data; } //------------------------------------------------- // data_w - data write //------------------------------------------------- void hd44102_device::data_w(u8 data) { m_ram[m_x][m_y] = data; count_up_or_down(); } //------------------------------------------------- // render - render the pixels //------------------------------------------------- const u8 *hd44102_device::render() { memset(m_render_buf, 0, sizeof(m_render_buf)); if (!(m_status & STATUS_DISPLAY_OFF)) { for (int x = 0; x < 50; x++) { int z = m_page << 3; for (int y = 0; y < 32; y++) { m_render_buf[z * 50 + x] = BIT(m_ram[z >> 3][x], z & 7); z = (z + 1) & 0x1f; } } } return m_render_buf; } //------------------------------------------------- // update_screen - update screen //------------------------------------------------- u32 hd44102_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { const u8 *src = render(); for (int x = 0; x < 50; x++) { for (int y = 0; y < 32; y++) { int sx = m_sx + x; int sy = m_sy + y; if (cliprect.contains(sx, sy)) bitmap.pix(sy, sx) = src[y * 50 + x]; } } return 0; }