summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/hd44780.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/video/hd44780.cpp')
-rw-r--r--src/devices/video/hd44780.cpp303
1 files changed, 236 insertions, 67 deletions
diff --git a/src/devices/video/hd44780.cpp b/src/devices/video/hd44780.cpp
index a2fdb2d3032..0c78d01da31 100644
--- a/src/devices/video/hd44780.cpp
+++ b/src/devices/video/hd44780.cpp
@@ -2,17 +2,124 @@
// copyright-holders:Sandro Ronco
/***************************************************************************
- Hitachi HD44780 LCD controller
-
- TODO:
- - dump internal CGROM
- - emulate osc pin, determine video timings and busy flag duration from it,
- and if possible, remove m_busy_factor
+ Hitachi HD44780 LCD controller
+
+ TODO:
+ - dump internal CGROM via decap
+ - finish emulating osc pin: determine video timings from it,
+ and if possible, remove m_busy_factor
+ The internal oscillator is known to be based on the value of the resistor
+ Rf, presumably (based on the HD44780U datasheet diagrams) with an
+ equation close to this:
+ For 5V power, Frequency in Hz = 1 / (2 * PI * Rf * 6.5pF)
+ For 3V power, Frequency in Hz = 1 / (2 * PI * Rf * 7.86pF)
+ The vast majority of devices use the internal oscillator with an Rf
+ resistor of 91kOhms, for ~270kHz clock speed.
+ However, a few Hitachi-made LCD modules use an Rf resistor of
+ 200kOhms instead, for a ~122.4kHz clock speed, and it isn't clear
+ why they did this, as it is lower than the minimum intended clock
+ speed on the datasheet.
+ DONE
+ - busy flag durations in clock cycles
+ - blink timing in clock cycles
+
+ BUSY TIMINGS:
+ Manufacturer: Hitachi Epson Samsung
+ Device: HD44780 SED1278 KS0066+S6A0069
+ Clear display: 410 cycles 410 cycles 410 cycles
+ Return Home: 410 cycles 410 cycles 410 cycles
+ Entry Mode: 10 cycles 10 cycles 10 cycles
+ Display on/off: 10 cycles 10 cycles 10 cycles
+ Cursor/Display Shift: 10 cycles 10 cycles 10 cycles
+ Function Set: 10 cycles 10 cycles 10 cycles
+ Set CGRAM: 10 cycles 10 cycles 10 cycles
+ Set DDRAM: 10 cycles 10 cycles 10 cycles
+ Data Write: 10 cycles[1] 10 cycles 11 cycles
+ Data Read: 10 cycles[1] 10 cycles 11 cycles[2,3]
+ Blink Invert Rate: 102400 cycles 102400 cycles ? cycles[4]
+ [1] HD44780 bug: BUSY is only active for 10 cycles, but the ac inc/dec on
+ data read or write happens on the 11th cycle.
+ This bug is documented in the later HD44780U datasheet, as well as in
+ the SED1278 datasheet to note the bug is fixed there.
+ We do not emulate this bug, but emulate as SED1278 where it is fixed.
+ [2] KS0066 bug: a Data read from CGRAM after a CGRAM write +
+ increment/decrement has completed will read the previous byte, not
+ the new one the address counter now points to!
+ This bug is documented in the datasheet.
+ [3] S6A0069 note: the first data read from CGRAM must have a set address
+ command or a display shift command sent before the read command, or
+ it will instead return the potentially garbage data that was most
+ recently put into the read buffer register as the serially-accessed
+ ram hasn't had a chance to fully complete a serial cycle and update
+ said buffer. Also, if the AC direction has not been explicitly set
+ beforehand by a display shift command, the byte address
+ auto-increment vs auto-decrement might be doing either one randomly
+ after power-up.
+ In short, this documentation change plus some minor behavior change
+ "fixes" the bug of the KS0066, with the above caveat.
+ [4] In all likelyhood, this is almost certainly 102400 for compatibility
+ with the original HD44780A00
+
+ Known HD44780-compatible clones:
+ Epson SED1278
+ Samsung KS0066[U]
+ Samsung S6A0069 (equivalent to KS0066U?)
+ Sitronix ST7066U
+ Sunplus SPLC780C (timings equivalent to SED1278?)
+
+ Possibly compatible clones:
+ Toshiba T1719A (may be custom for Brother)
+
+ Similar, but not quite compatible variants:
+ Hitachi HD66780 LCD-IIA (more or less the same as 44780 but has a
+ different LCD driver waveform, and like 44780 has
+ 16 Common/40 Segment drivers)
+ Samsung KS0073 (uses different DPRAM memory mapping than HD44780, and
+ has 34 Common/64 Segment drivers; it also has a low power mode and
+ 6-pixel character width modes)
+ Samsung S6A0073 (equivalent to KS0073)
+ Novatek NT7603 (clocked at twice the speed, has different LCD driver
+ waveforms, and has 16 Common/80 Segment drivers)
+ Novatek NT7605 (clocked at twice the speed, has different LCD driver
+ waveforms, and has 16 Common/100 Segment drivers)
+ Solomon Systech SSD1803 (2.7-3.45V only, 34 Common/100 Segment drivers,
+ has a 6-pixel character width mode, and when in 4-line mode, has a
+ memory map like KS0073; has two unique CGROMs)
+
+
+ Character set equivalency:
+ Hitachi Samsung Epson Sitronix Sunplus [Supported]
+ HD44780A00 KS0066F00 SED1278F0A ST7066-0A SPLC780C-01 Y
+ HD44780UA00 Y
+ HD44780UA01
+ SPLC780C-17
+ HD44780UA02 ST7066-0R Y
+ SED1278F0H ST7066-0T SPLC780C-02
+ SED1278F0B ST7066-0B SPLC780C-03 Y
+ KS0066F03 SPLC780C-08
+ KS0066F04 SPLC780C-13
+ KS0066F05 ST7066-0E SPLC780C-11 Y
+ KS0066F06 SPLC780C-12
+ ST7066-1G SPLC780C-14
+ SPLC780C-15
+ SPLC780C-19
+ SED1278F0C
+ KS0066F59 SED1278F0E
+ SED1278F0G SPLC780C-18
+
+
+ DPRAM 80-byte wrap behavior is based on increment/decrement:
+ In 1-line mode:
+ 4E 4F 00 01 02 ... 4D 4E 4F 00 01 02 ...
+
+ In 2-line mode:
+ L1: 26 27 00 01 02 ... 24 25 26 27 00 01 02 ...
+ L2: 66 67 40 41 42 ... 64 65 66 67 40 41 42 ...
***************************************************************************/
#include "emu.h"
-#include "video/hd44780.h"
+#include "hd44780.h"
//#define VERBOSE 1
#include "logmacro.h"
@@ -22,30 +129,72 @@
// DEVICE DEFINITIONS
//**************************************************************************
-DEFINE_DEVICE_TYPE(HD44780, hd44780_device, "hd44780_a00", "Hitachi HD44780 A00 LCD Controller")
-DEFINE_DEVICE_TYPE(SED1278_0B, sed1278_0b_device, "sed1278_0b", "Epson SED1278-0B LCD Controller") // packaged as either SED1278F0B or SED1278D0B
-DEFINE_DEVICE_TYPE(KS0066_F05, ks0066_f05_device, "ks0066_f05", "Samsung KS0066 F05 LCD Controller")
+DEFINE_DEVICE_TYPE(HD44780, hd44780_device, "hd44780", "Hitachi HD44780 LCD Controller")
+DEFINE_DEVICE_TYPE(HD44780U, hd44780u_device, "hd44780u", "Hitachi HD44780U LCD Controller")
+DEFINE_DEVICE_TYPE(SED1278, sed1278_device, "sed1278", "Epson SED1278 LCD Controller") // packaged as either SED1278F0B or SED1278D0B
+DEFINE_DEVICE_TYPE(KS0066, ks0066_device, "ks0066", "Samsung KS0066 LCD Controller")
//-------------------------------------------------
// ROM( hd44780 )
//-------------------------------------------------
-ROM_START( hd44780_a00 )
+ROM_START( hd44780 )
+ ROM_DEFAULT_BIOS("a00")
+ ROM_SYSTEM_BIOS(0, "a00", "A00")
+
+ ROM_REGION( 0x1000, "cgrom", 0 )
+ ROMX_LOAD( "hd44780_a00.bin", 0x0000, 0x1000, BAD_DUMP CRC(e459877c) SHA1(65cf075a988cdcbb316b9afdd0529b374a1a65ec), ROM_BIOS(0)) // from page 97 of the 1985 HD44780 datasheet from crystalfontz
+ROM_END
+
+ROM_START( hd44780u )
+ ROM_DEFAULT_BIOS("a00")
+ ROM_SYSTEM_BIOS(0, "a00", "A00")
+ ROM_SYSTEM_BIOS(1, "a02", "A02")
+ // Note the HD44780UA01 font does exist in the 1994 M24T026 Hitachi LCD Controller Driver LSI Data Book on bitsavers as well
+
ROM_REGION( 0x1000, "cgrom", 0 )
- ROM_LOAD( "hd44780_a00.bin", 0x0000, 0x1000, BAD_DUMP CRC(01d108e2) SHA1(bc0cdf0c9ba895f22e183c7bd35a3f655f2ca96f)) // from page 17 of the HD44780 datasheet
+ ROMX_LOAD( "hd44780u_a00.bin", 0x0000, 0x1000, BAD_DUMP CRC(8494cb6b) SHA1(2d4f9cf5ff81f20d2f4e4640f5b8a697a3781eef), ROM_BIOS(0)) // from page 17 of the 1999 HD44780U datasheet
+ // this has 6 characters different from the HD44780A00
+ // this differs slightly (see the '7') from the HD44780UA00 font in the 1994 M24T026 Hitachi LCD Controller Driver LSI Data Book on bitsavers
+ // and needs confirmation from a real device as to which is correct; confirmation on the horizontal offset of the '[' would also be helpful
+ ROMX_LOAD( "hd44780u_a02.bin", 0x0000, 0x1000, BAD_DUMP CRC(6d522b42) SHA1(db8f59573c81933cfc9d3232d419406f0896e60b), ROM_BIOS(1)) // from page 18 of the 1999 HD44780U datasheet
+ // this differs slightly (see the '7') from the HD44780UA02 font in the 1994 M24T026 Hitachi LCD Controller Driver LSI Data Book on bitsavers
+ // and needs confirmation from a real device as to which is correct
ROM_END
-ROM_START( sed1278_0b )
+
+ROM_START( sed1278 )
+ ROM_DEFAULT_BIOS("0a")
+ ROM_SYSTEM_BIOS(0, "0a", "0A")
+ ROM_SYSTEM_BIOS(1, "0b", "0B")
+
ROM_REGION( 0x1000, "cgrom", 0 )
- ROM_LOAD( "sed1278_0b.bin", 0x0000, 0x1000, BAD_DUMP CRC(eef342fa) SHA1(d6ac58a48e428e7cff26fb9c8ea9b4eeaa853038)) // from page 9-33 of the SED1278 datasheet
+ ROMX_LOAD( "sed1278_0a.bin", 0x0000, 0x1000, BAD_DUMP CRC(e459877c) SHA1(65cf075a988cdcbb316b9afdd0529b374a1a65ec), ROM_BIOS(0)) // from page 9-32 of the SED1278 datasheet (100% identical to hd44780a00)
+ ROMX_LOAD( "sed1278_0b.bin", 0x0000, 0x1000, BAD_DUMP CRC(962498b7) SHA1(41866836ab4ed7bd4c3539bc8df492ba7d7ff42a), ROM_BIOS(1)) // from page 9-33 of the SED1278 datasheet
ROM_END
-ROM_START( ks0066_f05 )
+// TODO: many other SED1278 variants, documented in the datasheet
+
+// ks0066_f00 is 100% identical to hd44780a00, see page 61 of the KS0066 non-U datasheet
+// later ks0066 masks may be different, see page 48 of the other KS0066 datasheet
+ROM_START( ks0066 )
+ ROM_DEFAULT_BIOS("f00")
+ ROM_SYSTEM_BIOS(0, "f00", "F00")
+ //ROM_SYSTEM_BIOS(?, "f03", "F03")
+ //ROM_SYSTEM_BIOS(?, "f04", "F04")
+ ROM_SYSTEM_BIOS(1, "f05", "F05")
+ //ROM_SYSTEM_BIOS(?, "f06", "F06")
+ //ROM_SYSTEM_BIOS(?, "f59", "F59")
+
ROM_REGION( 0x1000, "cgrom", 0 )
- ROM_LOAD( "ks0066_f05.bin", 0x0000, 0x1000, BAD_DUMP CRC(af9e7bd6) SHA1(0196e871584ee5d370856e7307c0f9d1466e3e51)) // from page 51 of the KS0066 datasheet
+ ROMX_LOAD( "ks0066_f00.bin", 0x0000, 0x1000, BAD_DUMP CRC(e459877c) SHA1(65cf075a988cdcbb316b9afdd0529b374a1a65ec), ROM_BIOS(0)) // from page 61 of the KS0066 non-U datasheet
+ ROMX_LOAD( "ks0066_f05.bin", 0x0000, 0x1000, BAD_DUMP CRC(af9e7bd6) SHA1(0196e871584ee5d370856e7307c0f9d1466e3e51), ROM_BIOS(1)) // from page 51 of the KS0066 datasheet
ROM_END
+
+// TODO: Sitronix and Sunplus devices/variants
+
//**************************************************************************
// live device
//**************************************************************************
@@ -57,7 +206,6 @@ ROM_END
hd44780_device::hd44780_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: hd44780_device(mconfig, HD44780, tag, owner, clock)
{
- set_charset_type(CHARSET_HD44780_A00);
}
hd44780_device::hd44780_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
@@ -74,16 +222,19 @@ hd44780_device::hd44780_device(const machine_config &mconfig, device_type type,
{
}
-sed1278_0b_device::sed1278_0b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- hd44780_device(mconfig, SED1278_0B, tag, owner, clock)
+hd44780u_device::hd44780u_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ hd44780_device(mconfig, HD44780U, tag, owner, clock)
{
- set_charset_type(CHARSET_SED1278_0B);
}
-ks0066_f05_device::ks0066_f05_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- hd44780_device(mconfig, KS0066_F05, tag, owner, clock)
+sed1278_device::sed1278_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ hd44780_device(mconfig, SED1278, tag, owner, clock)
+{
+}
+
+ks0066_device::ks0066_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ hd44780_device(mconfig, KS0066, tag, owner, clock)
{
- set_charset_type(CHARSET_KS0066_F05);
}
@@ -93,14 +244,22 @@ ks0066_f05_device::ks0066_f05_device(const machine_config &mconfig, const char *
const tiny_rom_entry *hd44780_device::device_rom_region() const
{
- switch (m_charset_type)
- {
- case CHARSET_HD44780_A00: return ROM_NAME( hd44780_a00 );
- case CHARSET_SED1278_0B: return ROM_NAME( sed1278_0b );
- case CHARSET_KS0066_F05: return ROM_NAME( ks0066_f05 );
- }
+ return ROM_NAME(hd44780);
+}
- return nullptr;
+const tiny_rom_entry *hd44780u_device::device_rom_region() const
+{
+ return ROM_NAME(hd44780u);
+}
+
+const tiny_rom_entry *sed1278_device::device_rom_region() const
+{
+ return ROM_NAME(sed1278);
+}
+
+const tiny_rom_entry *ks0066_device::device_rom_region() const
+{
+ return ROM_NAME(ks0066);
}
//-------------------------------------------------
@@ -113,9 +272,8 @@ void hd44780_device::device_start()
m_pixel_update_cb.resolve();
- m_busy_timer = timer_alloc(TIMER_BUSY);
- m_blink_timer = timer_alloc(TIMER_BLINKING);
- m_blink_timer->adjust(attotime::from_msec(409), 0, attotime::from_msec(409));
+ m_busy_timer = timer_alloc(FUNC(hd44780_device::clear_busy_flag), this);
+ m_blink_timer = timer_alloc(FUNC(hd44780_device::blink_tick), this);
// state saving
save_item(NAME(m_busy_factor));
@@ -173,26 +331,44 @@ void hd44780_device::device_reset()
m_rs_state = 0;
m_rw_state = 0;
- set_busy_flag(1520);
+ set_busy_flag(410);
}
+//-------------------------------------------------
+// device_clock_changed
+//-------------------------------------------------
+
+void hd44780_device::device_clock_changed()
+{
+ // (re)adjust blink timer
+ attotime period = attotime::from_ticks(102400, clock()); // blink happens every 102400 cycles
+ attotime remain = m_blink_timer->remaining();
+
+ m_blink_timer->adjust((remain > period) ? period : remain, 0, period);
+}
//-------------------------------------------------
-// device_timer - handler timer events
+// device validity check
//-------------------------------------------------
-void hd44780_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+void hd44780_device::device_validity_check(validity_checker &valid) const
{
- switch (id)
- {
- case TIMER_BUSY:
- m_busy_flag = false;
- break;
+ if (clock() == 0)
+ osd_printf_error("LCDC clock cannot be zero!\n");
+}
- case TIMER_BLINKING:
- m_blink = !m_blink;
- break;
- }
+//-------------------------------------------------
+// timer events
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(hd44780_device::clear_busy_flag)
+{
+ m_busy_flag = false;
+}
+
+TIMER_CALLBACK_MEMBER(hd44780_device::blink_tick)
+{
+ m_blink = !m_blink;
}
@@ -200,17 +376,10 @@ void hd44780_device::device_timer(emu_timer &timer, device_timer_id id, int para
// HELPERS
//**************************************************************************
-void hd44780_device::set_charset_type(int type)
-{
- m_charset_type = type;
-}
-
-void hd44780_device::set_busy_flag(uint16_t usec)
+void hd44780_device::set_busy_flag(uint16_t cycles)
{
m_busy_flag = true;
-
- usec = float(usec) * m_busy_factor + 0.5;
- m_busy_timer->adjust(attotime::from_usec(usec));
+ m_busy_timer->adjust(attotime::from_ticks(cycles, clock() / m_busy_factor));
}
void hd44780_device::correct_ac()
@@ -418,17 +587,17 @@ void hd44780_device::db_w(u8 data)
m_db_input = data;
}
-WRITE_LINE_MEMBER(hd44780_device::rs_w)
+void hd44780_device::rs_w(int state)
{
m_rs_input = state;
}
-WRITE_LINE_MEMBER(hd44780_device::rw_w)
+void hd44780_device::rw_w(int state)
{
m_rw_input = state;
}
-WRITE_LINE_MEMBER(hd44780_device::e_w)
+void hd44780_device::e_w(int state)
{
if (m_data_len == 4 && state && !m_enabled && !machine().side_effects_disabled())
update_nibble(m_rs_input, m_rw_input);
@@ -470,7 +639,7 @@ void hd44780_device::control_write(u8 data)
m_active_ram = DDRAM;
m_ac = m_ir & 0x7f;
correct_ac();
- set_busy_flag(37);
+ set_busy_flag(10);
LOG("HD44780: set DDRAM address %x\n", m_ac);
return;
@@ -480,7 +649,7 @@ void hd44780_device::control_write(u8 data)
// set CGRAM address
m_active_ram = CGRAM;
m_ac = m_ir & 0x3f;
- set_busy_flag(37);
+ set_busy_flag(10);
LOG("HD44780: set CGRAM address %x\n", m_ac);
return;
@@ -499,7 +668,7 @@ void hd44780_device::control_write(u8 data)
m_data_len = BIT(m_ir, 4) ? 8 : 4;
m_num_line = BIT(m_ir, 3) + 1;
correct_ac();
- set_busy_flag(37);
+ set_busy_flag(10);
LOG("HD44780: char size 5x%d, data len %d, lines %d\n", m_char_size, m_data_len, m_num_line);
return;
@@ -516,7 +685,7 @@ void hd44780_device::control_write(u8 data)
else
update_ac(direction);
- set_busy_flag(37);
+ set_busy_flag(10);
}
else if (BIT(m_ir, 3))
{
@@ -524,7 +693,7 @@ void hd44780_device::control_write(u8 data)
m_display_on = BIT(m_ir, 2);
m_cursor_on = BIT(m_ir, 1);
m_blink_on = BIT(m_ir, 0);
- set_busy_flag(37);
+ set_busy_flag(10);
LOG("HD44780: display %d, cursor %d, blink %d\n", m_display_on, m_cursor_on, m_blink_on);
}
@@ -533,7 +702,7 @@ void hd44780_device::control_write(u8 data)
// entry mode set
m_direction = (BIT(m_ir, 1)) ? +1 : -1;
m_shift_on = BIT(m_ir, 0);
- set_busy_flag(37);
+ set_busy_flag(10);
LOG("HD44780: entry mode set: direction %d, shift %d\n", m_direction, m_shift_on);
}
@@ -546,7 +715,7 @@ void hd44780_device::control_write(u8 data)
m_active_ram = DDRAM;
m_direction = 1;
m_disp_shift = 0;
- set_busy_flag(1520);
+ set_busy_flag(410);
}
else if (BIT(m_ir, 0))
{
@@ -558,7 +727,7 @@ void hd44780_device::control_write(u8 data)
m_direction = 1;
m_disp_shift = 0;
memset(m_ddram, 0x20, sizeof(m_ddram));
- set_busy_flag(1520);
+ set_busy_flag(410);
// Some machines do a "clear display" first, even though the datasheet insists "function set" must come before all else
return;
@@ -614,10 +783,10 @@ void hd44780_device::data_write(u8 data)
else
m_cgram[m_ac] = m_dr;
+ set_busy_flag(10);
update_ac(m_direction);
if (m_shift_on)
shift_display(m_direction);
- set_busy_flag(41);
}
u8 hd44780_device::data_read()
@@ -636,8 +805,8 @@ u8 hd44780_device::data_read()
if (!machine().side_effects_disabled())
{
+ set_busy_flag(10);
update_ac(m_direction);
- set_busy_flag(41);
}
return data;