1 // license:BSD-3-Clause
2 // copyright-holders:Sandro Ronco
3 /***************************************************************************
4 
5         Hitachi HD44780 LCD controller
6 
7         TODO:
8         - dump internal CGROM
9         - emulate osc pin, determine video timings and busy flag duration from it,
10           and if possible, remove m_busy_factor
11 
12 ***************************************************************************/
13 
14 #include "emu.h"
15 #include "video/hd44780.h"
16 
17 //#define VERBOSE 1
18 #include "logmacro.h"
19 
20 
21 //**************************************************************************
22 //  DEVICE DEFINITIONS
23 //**************************************************************************
24 
25 DEFINE_DEVICE_TYPE(HD44780,    hd44780_device,    "hd44780_a00", "Hitachi HD44780 A00 LCD Controller")
26 DEFINE_DEVICE_TYPE(SED1278_0B, sed1278_0b_device, "sed1278_0b",  "Epson SED1278-0B LCD Controller") // packaged as either SED1278F0B or SED1278D0B
27 DEFINE_DEVICE_TYPE(KS0066_F05, ks0066_f05_device, "ks0066_f05",  "Samsung KS0066 F05 LCD Controller")
28 
29 
30 //-------------------------------------------------
31 //  ROM( hd44780 )
32 //-------------------------------------------------
33 
ROM_START(hd44780_a00)34 ROM_START( hd44780_a00 )
35 	ROM_REGION( 0x1000, "cgrom", 0 )
36 	ROM_LOAD( "hd44780_a00.bin",    0x0000, 0x1000,  BAD_DUMP CRC(01d108e2) SHA1(bc0cdf0c9ba895f22e183c7bd35a3f655f2ca96f)) // from page 17 of the HD44780 datasheet
37 ROM_END
38 
39 ROM_START( sed1278_0b )
40 	ROM_REGION( 0x1000, "cgrom", 0 )
41 	ROM_LOAD( "sed1278_0b.bin",    0x0000, 0x1000,  BAD_DUMP CRC(eef342fa) SHA1(d6ac58a48e428e7cff26fb9c8ea9b4eeaa853038)) // from page 9-33 of the SED1278 datasheet
42 ROM_END
43 
44 ROM_START( ks0066_f05 )
45 	ROM_REGION( 0x1000, "cgrom", 0 )
46 	ROM_LOAD( "ks0066_f05.bin",    0x0000, 0x1000,  BAD_DUMP CRC(af9e7bd6) SHA1(0196e871584ee5d370856e7307c0f9d1466e3e51)) // from page 51 of the KS0066 datasheet
47 ROM_END
48 
49 //**************************************************************************
50 //  live device
51 //**************************************************************************
52 
53 //-------------------------------------------------
54 //  hd44780_device - constructor
55 //-------------------------------------------------
56 
57 hd44780_device::hd44780_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
58 	: hd44780_device(mconfig, HD44780, tag, owner, clock)
59 {
60 	set_charset_type(CHARSET_HD44780_A00);
61 }
62 
hd44780_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)63 hd44780_device::hd44780_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
64 	: device_t(mconfig, type, tag, owner, clock)
65 	, m_pixel_update_cb(*this)
66 	, m_busy_factor(1.0)
67 	, m_cgrom(nullptr)
68 	, m_cgrom_region(*this, DEVICE_SELF)
69 	, m_rs_input(0)
70 	, m_rw_input(0)
71 	, m_db_input(0)
72 	, m_enabled(false)
73 {
74 }
75 
sed1278_0b_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)76 sed1278_0b_device::sed1278_0b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
77 	hd44780_device(mconfig, SED1278_0B, tag, owner, clock)
78 {
79 	set_charset_type(CHARSET_SED1278_0B);
80 }
81 
ks0066_f05_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)82 ks0066_f05_device::ks0066_f05_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
83 	hd44780_device(mconfig, KS0066_F05, tag, owner, clock)
84 {
85 	set_charset_type(CHARSET_KS0066_F05);
86 }
87 
88 
89 //-------------------------------------------------
90 //  rom_region - device-specific ROM region
91 //-------------------------------------------------
92 
device_rom_region() const93 const tiny_rom_entry *hd44780_device::device_rom_region() const
94 {
95 	switch (m_charset_type)
96 	{
97 		case CHARSET_HD44780_A00:   return ROM_NAME( hd44780_a00 );
98 		case CHARSET_SED1278_0B:    return ROM_NAME( sed1278_0b );
99 		case CHARSET_KS0066_F05:    return ROM_NAME( ks0066_f05 );
100 	}
101 
102 	return nullptr;
103 }
104 
105 //-------------------------------------------------
106 //  device_start - device-specific startup
107 //-------------------------------------------------
108 
device_start()109 void hd44780_device::device_start()
110 {
111 	m_cgrom = m_cgrom_region.found() ? m_cgrom_region : memregion("cgrom")->base();
112 
113 	m_pixel_update_cb.resolve();
114 
115 	m_busy_timer = timer_alloc(TIMER_BUSY);
116 	m_blink_timer = timer_alloc(TIMER_BLINKING);
117 	m_blink_timer->adjust(attotime::from_msec(409), 0, attotime::from_msec(409));
118 
119 	// state saving
120 	save_item(NAME(m_busy_factor));
121 	save_item(NAME(m_busy_flag));
122 	save_item(NAME(m_ac));
123 	save_item(NAME(m_dr));
124 	save_item(NAME(m_ir));
125 	save_item(NAME(m_active_ram));
126 	save_item(NAME(m_display_on));
127 	save_item(NAME(m_cursor_on));
128 	save_item(NAME(m_shift_on));
129 	save_item(NAME(m_blink_on));
130 	save_item(NAME(m_direction));
131 	save_item(NAME(m_data_len));
132 	save_item(NAME(m_num_line));
133 	save_item(NAME(m_char_size));
134 	save_item(NAME(m_disp_shift));
135 	save_item(NAME(m_blink));
136 	save_item(NAME(m_ddram));
137 	save_item(NAME(m_cgram));
138 	save_item(NAME(m_nibble));
139 	save_item(NAME(m_rs_input));
140 	save_item(NAME(m_rw_input));
141 	save_item(NAME(m_db_input));
142 	save_item(NAME(m_enabled));
143 	save_item(NAME(m_rs_state));
144 	save_item(NAME(m_rw_state));
145 }
146 
147 //-------------------------------------------------
148 //  device_reset - device-specific reset
149 //-------------------------------------------------
150 
device_reset()151 void hd44780_device::device_reset()
152 {
153 	memset(m_ddram, 0x20, sizeof(m_ddram)); // filled with SPACE char
154 	memset(m_cgram, 0, sizeof(m_cgram));
155 
156 	m_ac         = 0;
157 	m_dr         = 0;
158 	m_ir         = 0;
159 	m_active_ram = DDRAM;
160 	m_display_on = false;
161 	m_cursor_on  = false;
162 	m_blink_on   = false;
163 	m_shift_on   = false;
164 	m_direction  = 1;
165 	m_data_len   = 8;
166 	m_num_line   = 1;
167 	m_char_size  = 8;
168 	m_disp_shift = 0;
169 	m_blink      = false;
170 	m_nibble     = false;
171 	m_first_cmd  = true;
172 	m_rs_state   = 0;
173 	m_rw_state   = 0;
174 
175 	set_busy_flag(1520);
176 }
177 
178 
179 //-------------------------------------------------
180 //  device_timer - handler timer events
181 //-------------------------------------------------
182 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)183 void hd44780_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
184 {
185 	switch (id)
186 	{
187 		case TIMER_BUSY:
188 			m_busy_flag = false;
189 			break;
190 
191 		case TIMER_BLINKING:
192 			m_blink = !m_blink;
193 			break;
194 	}
195 }
196 
197 
198 //**************************************************************************
199 //  HELPERS
200 //**************************************************************************
201 
set_charset_type(int type)202 void hd44780_device::set_charset_type(int type)
203 {
204 	m_charset_type = type;
205 }
206 
set_busy_flag(uint16_t usec)207 void hd44780_device::set_busy_flag(uint16_t usec)
208 {
209 	m_busy_flag = true;
210 
211 	usec = float(usec) * m_busy_factor + 0.5;
212 	m_busy_timer->adjust(attotime::from_usec(usec));
213 }
214 
correct_ac()215 void hd44780_device::correct_ac()
216 {
217 	if (m_active_ram == DDRAM)
218 	{
219 		int max_ac = (m_num_line == 1) ? 0x4f : 0x67;
220 
221 		if (m_ac > max_ac)
222 			m_ac -= max_ac + 1;
223 		else if (m_ac < 0)
224 			m_ac = max_ac;
225 		else if (m_num_line == 2 && m_ac > 0x27 && m_ac < 0x40)
226 			m_ac = 0x40 + (m_ac - 0x28);
227 	}
228 	else
229 		m_ac &= 0x3f;
230 }
231 
update_ac(int direction)232 void hd44780_device::update_ac(int direction)
233 {
234 	if (m_active_ram == DDRAM && m_num_line == 2 && direction == -1 && m_ac == 0x40)
235 		m_ac = 0x27;
236 	else
237 		m_ac += direction;
238 
239 	correct_ac();
240 }
241 
shift_display(int direction)242 void hd44780_device::shift_display(int direction)
243 {
244 	m_disp_shift += direction;
245 
246 	if (m_disp_shift == 0x50)
247 		m_disp_shift = 0;
248 	else if (m_disp_shift == -1)
249 		m_disp_shift = 0x4f;
250 }
251 
update_nibble(int rs,int rw)252 void hd44780_device::update_nibble(int rs, int rw)
253 {
254 	if (m_rs_state != rs || m_rw_state != rw)
255 	{
256 		m_rs_state = rs;
257 		m_rw_state = rw;
258 		m_nibble = false;
259 	}
260 
261 	m_nibble = !m_nibble;
262 }
263 
pixel_update(bitmap_ind16 & bitmap,u8 line,u8 pos,u8 y,u8 x,int state)264 inline void hd44780_device::pixel_update(bitmap_ind16 &bitmap, u8 line, u8 pos, u8 y, u8 x, int state)
265 {
266 	if (!m_pixel_update_cb.isnull())
267 	{
268 		m_pixel_update_cb(bitmap, line, pos, y, x, state);
269 	}
270 	else
271 	{
272 		u8 line_height = (m_char_size == 8) ? m_char_size : m_char_size + 1;
273 
274 		if (m_lines <= 2)
275 		{
276 			if (pos < m_chars)
277 				bitmap.pix(line * (line_height + 1) + y, pos * 6 + x) = state;
278 		}
279 		else if (m_lines <= 4)
280 		{
281 			if (pos < m_chars*2)
282 			{
283 				if (pos >= m_chars)
284 				{
285 					line += 2;
286 					pos -= m_chars;
287 				}
288 
289 				if (line < m_lines)
290 					bitmap.pix(line * (line_height + 1) + y, pos * 6 + x) = state;
291 			}
292 		}
293 		else
294 		{
295 			fatalerror("%s: use a custom callback for this LCD configuration (%d x %d)\n", tag(), m_lines, m_chars);
296 		}
297 	}
298 }
299 
300 
301 //**************************************************************************
302 //  device interface
303 //**************************************************************************
304 
render()305 const u8 *hd44780_device::render()
306 {
307 	memset(m_render_buf, 0, sizeof(m_render_buf));
308 
309 	if (m_display_on)
310 	{
311 		u8 line_size = 80 / m_num_line;
312 
313 		for (int line = 0; line < m_num_line; line++)
314 		{
315 			for (int pos = 0; pos < line_size; pos++)
316 			{
317 				uint16_t char_pos = line * 0x40 + ((pos + m_disp_shift) % line_size);
318 
319 				int char_base;
320 				if (m_ddram[char_pos] < 0x10)
321 				{
322 					// draw CGRAM characters
323 					if (m_char_size == 8)
324 						char_base = (m_ddram[char_pos] & 0x07) * 8;
325 					else
326 						char_base = ((m_ddram[char_pos] >> 1) & 0x03) * 16;
327 				}
328 				else
329 				{
330 					// draw CGROM characters
331 					char_base = m_ddram[char_pos] * 0x10;
332 				}
333 
334 				const u8 *charset = (m_ddram[char_pos] < 0x10) ? m_cgram : m_cgrom;
335 				u8 *dest = m_render_buf + 16 * (line * line_size + pos);
336 				memcpy (dest, charset + char_base, m_char_size);
337 
338 				if (char_pos == m_ac)
339 				{
340 					// draw the cursor
341 					if (m_cursor_on)
342 						dest[m_char_size - 1] = 0x1f;
343 
344 					if (!m_blink && m_blink_on)
345 						memset(dest, 0x1f, m_char_size);
346 				}
347 			}
348 		}
349 	}
350 
351 	return m_render_buf;
352 }
353 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)354 uint32_t hd44780_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
355 {
356 	bitmap.fill(0, cliprect);
357 	const u8 *img = render();
358 
359 	u8 line_size = 80 / m_num_line;
360 
361 	for (int line = 0; line < m_num_line; line++)
362 	{
363 		for (int pos = 0; pos < line_size; pos++)
364 		{
365 			const u8 *src = img + 16 * (line * line_size + pos);
366 			for (int y = 0; y < m_char_size; y++)
367 				for (int x = 0; x < 5; x++)
368 					pixel_update(bitmap, line, pos, y, x, BIT(src[y], 4 - x));
369 		}
370 	}
371 
372 	return 0;
373 }
374 
read(offs_t offset)375 u8 hd44780_device::read(offs_t offset)
376 {
377 	if (m_data_len == 4 && !machine().side_effects_disabled())
378 		update_nibble(offset & 0x01, 1);
379 
380 	switch (offset & 0x01)
381 	{
382 		case 0: return control_read();
383 		case 1: return data_read();
384 	}
385 
386 	return 0;
387 }
388 
write(offs_t offset,u8 data)389 void hd44780_device::write(offs_t offset, u8 data)
390 {
391 	if (m_data_len == 4 && !machine().side_effects_disabled())
392 		update_nibble(offset & 0x01, 0);
393 
394 	switch (offset & 0x01)
395 	{
396 		case 0: control_write(data);  break;
397 		case 1: data_write(data);     break;
398 	}
399 }
400 
db_r()401 u8 hd44780_device::db_r()
402 {
403 	if (m_enabled && m_rw_input == 1)
404 	{
405 		switch (m_rs_input)
406 		{
407 			case 0: return control_read();
408 			case 1: return data_read();
409 		}
410 	}
411 
412 	return 0xff;
413 }
414 
db_w(u8 data)415 void hd44780_device::db_w(u8 data)
416 {
417 	m_db_input = data;
418 }
419 
WRITE_LINE_MEMBER(hd44780_device::rs_w)420 WRITE_LINE_MEMBER(hd44780_device::rs_w)
421 {
422 	m_rs_input = state;
423 }
424 
WRITE_LINE_MEMBER(hd44780_device::rw_w)425 WRITE_LINE_MEMBER(hd44780_device::rw_w)
426 {
427 	m_rw_input = state;
428 }
429 
WRITE_LINE_MEMBER(hd44780_device::e_w)430 WRITE_LINE_MEMBER(hd44780_device::e_w)
431 {
432 	if (m_data_len == 4 && state && !m_enabled && !machine().side_effects_disabled())
433 		update_nibble(m_rs_input, m_rw_input);
434 
435 	if (!state && m_enabled && m_rw_input == 0)
436 	{
437 		switch (m_rs_input)
438 		{
439 			case 0: control_write(m_db_input);  break;
440 			case 1: data_write(m_db_input);     break;
441 		}
442 	}
443 
444 	m_enabled = state;
445 }
446 
control_write(u8 data)447 void hd44780_device::control_write(u8 data)
448 {
449 	if (m_data_len == 4)
450 	{
451 		if (m_nibble)
452 		{
453 			m_ir = data & 0xf0;
454 			return;
455 		}
456 		else
457 		{
458 			m_ir |= ((data >> 4) & 0x0f);
459 		}
460 	}
461 	else
462 	{
463 		m_ir = data;
464 	}
465 
466 	if (BIT(m_ir, 7))
467 	{
468 		// set DDRAM address
469 		m_active_ram = DDRAM;
470 		m_ac = m_ir & 0x7f;
471 		correct_ac();
472 		set_busy_flag(37);
473 
474 		LOG("HD44780: set DDRAM address %x\n", m_ac);
475 		return;
476 	}
477 	else if (BIT(m_ir, 6))
478 	{
479 		// set CGRAM address
480 		m_active_ram = CGRAM;
481 		m_ac = m_ir & 0x3f;
482 		set_busy_flag(37);
483 
484 		LOG("HD44780: set CGRAM address %x\n", m_ac);
485 		return;
486 	}
487 	else if (BIT(m_ir, 5))
488 	{
489 		// function set
490 		if (!m_first_cmd && m_data_len == (BIT(m_ir, 4) ? 8 : 4) && (m_char_size != (BIT(m_ir, 2) ? 10 : 8) || m_num_line != (BIT(m_ir, 3) + 1)))
491 		{
492 			logerror("HD44780: function set cannot be executed after other instructions unless the interface data length is changed\n");
493 			return;
494 		}
495 		m_first_cmd = true;
496 
497 		m_char_size = BIT(m_ir, 2) ? 10 : 8;
498 		m_data_len  = BIT(m_ir, 4) ? 8 : 4;
499 		m_num_line  = BIT(m_ir, 3) + 1;
500 		correct_ac();
501 		set_busy_flag(37);
502 
503 		LOG("HD44780: char size 5x%d, data len %d, lines %d\n", m_char_size, m_data_len, m_num_line);
504 		return;
505 	}
506 	else if (BIT(m_ir, 4))
507 	{
508 		// cursor or display shift
509 		int direction = (BIT(m_ir, 2)) ? +1 : -1;
510 
511 		LOG("HD44780: %s shift %d\n", BIT(m_ir, 3) ? "display" : "cursor", direction);
512 
513 		if (BIT(m_ir, 3))
514 			shift_display(direction);
515 		else
516 			update_ac(direction);
517 
518 		set_busy_flag(37);
519 	}
520 	else if (BIT(m_ir, 3))
521 	{
522 		// display on/off control
523 		m_display_on = BIT(m_ir, 2);
524 		m_cursor_on  = BIT(m_ir, 1);
525 		m_blink_on   = BIT(m_ir, 0);
526 		set_busy_flag(37);
527 
528 		LOG("HD44780: display %d, cursor %d, blink %d\n", m_display_on, m_cursor_on, m_blink_on);
529 	}
530 	else if (BIT(m_ir, 2))
531 	{
532 		// entry mode set
533 		m_direction = (BIT(m_ir, 1)) ? +1 : -1;
534 		m_shift_on  = BIT(m_ir, 0);
535 		set_busy_flag(37);
536 
537 		LOG("HD44780: entry mode set: direction %d, shift %d\n", m_direction, m_shift_on);
538 	}
539 	else if (BIT(m_ir, 1))
540 	{
541 		// return home
542 		LOG("HD44780: return home\n");
543 
544 		m_ac         = 0;
545 		m_active_ram = DDRAM;
546 		m_direction  = 1;
547 		m_disp_shift = 0;
548 		set_busy_flag(1520);
549 	}
550 	else if (BIT(m_ir, 0))
551 	{
552 		// clear display
553 		LOG("HD44780: clear display\n");
554 
555 		m_ac         = 0;
556 		m_active_ram = DDRAM;
557 		m_direction  = 1;
558 		m_disp_shift = 0;
559 		memset(m_ddram, 0x20, sizeof(m_ddram));
560 		set_busy_flag(1520);
561 
562 		// Some machines do a "clear display" first, even though the datasheet insists "function set" must come before all else
563 		return;
564 	}
565 
566 	m_first_cmd = false;
567 }
568 
control_read()569 u8 hd44780_device::control_read()
570 {
571 	if (m_data_len == 4)
572 	{
573 		if (m_nibble)
574 			return (m_busy_flag ? 0x80 : 0) | (m_ac & 0x70);
575 		else
576 			return (m_ac << 4) & 0xf0;
577 	}
578 	else
579 	{
580 		return (m_busy_flag ? 0x80 : 0) | (m_ac & 0x7f);
581 	}
582 }
583 
data_write(u8 data)584 void hd44780_device::data_write(u8 data)
585 {
586 	if (m_busy_flag)
587 	{
588 		logerror("HD44780: Ignoring data write %02x due of busy flag\n", data);
589 		return;
590 	}
591 
592 	if (m_data_len == 4)
593 	{
594 		if (m_nibble)
595 		{
596 			m_dr = data & 0xf0;
597 			return;
598 		}
599 		else
600 		{
601 			m_dr |= ((data >> 4) & 0x0f);
602 		}
603 	}
604 	else
605 	{
606 		m_dr = data;
607 	}
608 
609 	LOG("HD44780: %sRAM write %x %x '%c'\n", m_active_ram == DDRAM ? "DD" : "CG", m_ac, m_dr, isprint(m_dr) ? m_dr : '.');
610 
611 	if (m_active_ram == DDRAM)
612 		m_ddram[m_ac] = m_dr;
613 	else
614 		m_cgram[m_ac] = m_dr;
615 
616 	update_ac(m_direction);
617 	if (m_shift_on)
618 		shift_display(m_direction);
619 	set_busy_flag(41);
620 }
621 
data_read()622 u8 hd44780_device::data_read()
623 {
624 	u8 data = (m_active_ram == DDRAM) ? m_ddram[m_ac] : m_cgram[m_ac];
625 
626 	LOG("HD44780: %sRAM read %x %c\n", m_active_ram == DDRAM ? "DD" : "CG", m_ac, data);
627 
628 	if (m_data_len == 4)
629 	{
630 		if (m_nibble)
631 			return data & 0xf0;
632 		else
633 			data = (data << 4) & 0xf0;
634 	}
635 
636 	if (!machine().side_effects_disabled())
637 	{
638 		update_ac(m_direction);
639 		set_busy_flag(41);
640 	}
641 
642 	return data;
643 }
644