1 // license:BSD-3-Clause
2 // copyright-holders:Sandro Ronco
3 /***************************************************************************
4 
5         Hitachi HD44352 LCD controller
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "video/hd44352.h"
11 
12 #define     LCD_BYTE_INPUT          0x01
13 #define     LCD_BYTE_OUTPUT         0x02
14 #define     LCD_CHAR_OUTPUT         0x03
15 #define     LCD_ON_OFF              0x04
16 #define     LCD_CURSOR_GRAPHIC      0x06
17 #define     LCD_CURSOR_CHAR         0x07
18 #define     LCD_SCROLL_CHAR_WIDTH   0x08
19 #define     LCD_CURSOR_STATUS       0x09
20 #define     LCD_USER_CHARACTER      0x0b
21 #define     LCD_CONTRAST            0x0c
22 #define     LCD_IRQ_FREQUENCY       0x0d
23 #define     LCD_CURSOR_POSITION     0x0e
24 
25 
26 // devices
27 DEFINE_DEVICE_TYPE(HD44352, hd44352_device, "hd44352", "Hitachi HD44352 LCD Controller")
28 
29 //**************************************************************************
30 //  live device
31 //**************************************************************************
32 
33 //-------------------------------------------------
34 //  hd44352_device - constructor
35 //-------------------------------------------------
36 
hd44352_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)37 hd44352_device::hd44352_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
38 	device_t(mconfig, HD44352, tag, owner, clock),
39 	m_on_cb(*this),
40 	m_char_rom(*this, DEVICE_SELF)
41 {
42 }
43 
44 //-------------------------------------------------
45 //  device_validity_check - perform validity checks
46 //  on this device
47 //-------------------------------------------------
48 
device_validity_check(validity_checker & valid) const49 void hd44352_device::device_validity_check(validity_checker &valid) const
50 {
51 }
52 //-------------------------------------------------
53 //  device_start - device-specific startup
54 //-------------------------------------------------
55 
device_start()56 void hd44352_device::device_start()
57 {
58 	m_on_cb.resolve_safe();
59 
60 	m_on_timer = timer_alloc(ON_TIMER);
61 	m_on_timer->adjust(attotime::from_hz(m_clock/16384), 0, attotime::from_hz(m_clock/16384));
62 
63 	save_item( NAME(m_control_lines));
64 	save_item( NAME(m_data_bus));
65 	save_item( NAME(m_state));
66 	save_item( NAME(m_offset));
67 	save_item( NAME(m_char_width));
68 	save_item( NAME(m_bank));
69 	save_item( NAME(m_lcd_on));
70 	save_item( NAME(m_scroll));
71 	save_item( NAME(m_contrast));
72 	save_item( NAME(m_byte_count));
73 	save_item( NAME(m_cursor_status));
74 	save_item( NAME(m_cursor_x));
75 	save_item( NAME(m_cursor_y));
76 	save_item( NAME(m_cursor_lcd));
77 	save_item( NAME(m_video_ram[0]));
78 	save_item( NAME(m_video_ram[1]));
79 	save_item( NAME(m_par));
80 	save_item( NAME(m_cursor));
81 	save_item( NAME(m_custom_char[0]));
82 	save_item( NAME(m_custom_char[1]));
83 	save_item( NAME(m_custom_char[2]));
84 	save_item( NAME(m_custom_char[3]));
85 }
86 
87 
88 //-------------------------------------------------
89 //  device_reset - device-specific reset
90 //-------------------------------------------------
91 
device_reset()92 void hd44352_device::device_reset()
93 {
94 	memset(m_video_ram, 0x00, sizeof(m_video_ram));
95 	memset(m_par, 0x00, sizeof(m_par));
96 	memset(m_custom_char, 0x00, sizeof(m_custom_char));
97 	memset(m_cursor, 0x00, sizeof(m_cursor));
98 	m_control_lines = 0;
99 	m_data_bus = 0xff;
100 	m_state = 0;
101 	m_bank = 0;
102 	m_offset = 0;
103 	m_char_width = 6;
104 	m_lcd_on = 0;
105 	m_scroll = 0;
106 	m_byte_count = 0;
107 	m_cursor_status = 0;
108 	m_cursor_x = 0;
109 	m_cursor_y = 0;
110 	m_cursor_lcd = 0;
111 	m_contrast = 0;
112 }
113 
114 
115 //-------------------------------------------------
116 //  device_timer - handler timer events
117 //-------------------------------------------------
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)118 void hd44352_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
119 {
120 	switch(id)
121 	{
122 		case ON_TIMER:
123 			if (m_control_lines & 0x40)
124 			{
125 				m_on_cb(ASSERT_LINE);
126 				m_on_cb(CLEAR_LINE);
127 			}
128 			break;
129 	}
130 }
131 
132 //**************************************************************************
133 //  device interface
134 //**************************************************************************
135 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)136 uint32_t hd44352_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
137 {
138 	uint8_t cw = m_char_width;
139 
140 	bitmap.fill(0, cliprect);
141 
142 	if (m_control_lines&0x80 && m_lcd_on)
143 	{
144 		for (int a=0; a<2; a++)
145 			for (int py=0; py<4; py++)
146 				for (int px=0; px<16; px++)
147 					if (BIT(m_cursor_status, 4) && px == m_cursor_x && py == m_cursor_y && a == m_cursor_lcd)
148 					{
149 						//draw the cursor
150 						for (int c=0; c<cw; c++)
151 						{
152 							uint8_t d = compute_newval((m_cursor_status>>5) & 0x07, m_video_ram[a][py*16*cw + px*cw + c + m_scroll * 48], m_cursor[c]);
153 							for (int b=0; b<8; b++)
154 							{
155 								bitmap.pix(py*8 + b, a*cw*16 + px*cw + c) = BIT(d, 7-b);
156 							}
157 						}
158 					}
159 					else
160 					{
161 						for (int c=0; c<cw; c++)
162 						{
163 							uint8_t d = m_video_ram[a][py*16*cw + px*cw + c + m_scroll * 48];
164 							for (int b=0; b<8; b++)
165 							{
166 								bitmap.pix(py*8 + b, a*cw*16 + px*cw + c) = BIT(d, 7-b);
167 							}
168 						}
169 					}
170 	}
171 
172 	return 0;
173 }
174 
175 
control_write(uint8_t data)176 void hd44352_device::control_write(uint8_t data)
177 {
178 	if(m_control_lines == data)
179 		m_state = 0;
180 
181 	m_control_lines = data;
182 }
183 
compute_newval(uint8_t type,uint8_t oldval,uint8_t newval)184 uint8_t hd44352_device::compute_newval(uint8_t type, uint8_t oldval, uint8_t newval)
185 {
186 	switch(type & 0x07)
187 	{
188 		case 0x00:
189 			return (~oldval) & newval;
190 		case 0x01:
191 			return oldval ^ newval;
192 		case 0x03:
193 			return oldval & (~newval);
194 		case 0x04:
195 			return newval;
196 		case 0x05:
197 			return oldval | newval;
198 		case 0x07:
199 			return oldval;
200 		case 0x02:
201 		case 0x06:
202 		default:
203 			return 0;
204 	}
205 }
206 
get_char(uint16_t pos)207 uint8_t hd44352_device::get_char(uint16_t pos)
208 {
209 	switch ((uint8_t)pos/8)
210 	{
211 		case 0xcf:
212 			return m_custom_char[0][pos%8];
213 		case 0xdf:
214 			return m_custom_char[1][pos%8];
215 		case 0xef:
216 			return m_custom_char[2][pos%8];
217 		case 0xff:
218 			return m_custom_char[3][pos%8];
219 		default:
220 			return m_char_rom[pos];
221 	}
222 }
223 
data_write(uint8_t data)224 void hd44352_device::data_write(uint8_t data)
225 {
226 	// verify that controller is active
227 	if (!(m_control_lines&0x80))
228 		return;
229 
230 	if (m_control_lines & 0x01)
231 	{
232 		if (!(m_control_lines&0x02) && !(m_control_lines&0x04))
233 			return;
234 
235 		switch (m_state)
236 		{
237 			case 0:     //parameter 0
238 				m_par[m_state++] = data;
239 				break;
240 			case 1:     //parameter 1
241 				m_par[m_state++] = data;
242 				break;
243 			case 2:     //parameter 2
244 				m_par[m_state++] = data;
245 				break;
246 		}
247 
248 		switch (m_par[0] & 0x0f)
249 		{
250 			case LCD_BYTE_INPUT:
251 			case LCD_CHAR_OUTPUT:
252 				{
253 					if (m_state == 1)
254 						m_bank = BIT(data, 4);
255 					else if (m_state == 2)
256 						m_offset = ((data>>1)&0x3f) % 48 + (BIT(data,7) * 48);
257 					else if (m_state == 3)
258 						m_offset += ((data & 0x03) * 96);
259 				}
260 				break;
261 			case LCD_BYTE_OUTPUT:
262 				{
263 					if (m_state == 1)
264 						m_bank = BIT(data, 4);
265 					else if (m_state == 2)
266 						m_offset = ((data>>1)&0x3f) % 48 + (BIT(data,7) * 48);
267 					else if (m_state == 3)
268 						m_offset += ((data & 0x03) * 96);
269 				}
270 				break;
271 			case LCD_ON_OFF:
272 				{
273 					if (m_state == 1)
274 						m_lcd_on = BIT(data, 4);
275 					m_data_bus = 0xff;
276 					m_state = 0;
277 				}
278 				break;
279 			case LCD_SCROLL_CHAR_WIDTH:
280 				{
281 					if (m_state == 1)
282 					{
283 						m_char_width = 8-((data>>4)&3);
284 						m_scroll = ((data>>6)&3);
285 					}
286 
287 					m_data_bus = 0xff;
288 					m_state = 0;
289 				}
290 				break;
291 			case LCD_CURSOR_STATUS:
292 				{
293 					if (m_state == 1)
294 						m_cursor_status = data;
295 					m_data_bus = 0xff;
296 					m_state = 0;
297 				}
298 				break;
299 			case LCD_CONTRAST:
300 				{
301 					if (m_state == 1)
302 						m_contrast = (m_contrast & 0x00ffff) | (data<<16);
303 					else if (m_state == 2)
304 						m_contrast = (m_contrast & 0xff00ff) | (data<<8);
305 					else if (m_state == 3)
306 					{
307 						m_contrast = (m_contrast & 0xffff00) | (data<<0);
308 						m_state = 0;
309 					}
310 
311 					m_data_bus = 0xff;
312 				}
313 				break;
314 			case LCD_IRQ_FREQUENCY:
315 				{
316 					if (m_state == 1)
317 					{
318 						uint32_t on_timer_rate;
319 
320 						switch((data>>4) & 0x0f)
321 						{
322 							case 0x00:      on_timer_rate = 16384;      break;
323 							case 0x01:      on_timer_rate = 8;          break;
324 							case 0x02:      on_timer_rate = 16;         break;
325 							case 0x03:      on_timer_rate = 32;         break;
326 							case 0x04:      on_timer_rate = 64;         break;
327 							case 0x05:      on_timer_rate = 128;        break;
328 							case 0x06:      on_timer_rate = 256;        break;
329 							case 0x07:      on_timer_rate = 512;        break;
330 							case 0x08:      on_timer_rate = 1024;       break;
331 							case 0x09:      on_timer_rate = 2048;       break;
332 							case 0x0a:      on_timer_rate = 4096;       break;
333 							case 0x0b:      on_timer_rate = 4096;       break;
334 							default:        on_timer_rate = 8192;       break;
335 						}
336 
337 						m_on_timer->adjust(attotime::from_hz(m_clock/on_timer_rate), 0, attotime::from_hz(m_clock/on_timer_rate));
338 					}
339 					m_data_bus = 0xff;
340 					m_state = 0;
341 				}
342 				break;
343 			case LCD_CURSOR_POSITION:
344 				{
345 					if (m_state == 1)
346 						m_cursor_lcd = BIT(data, 4);    //0:left lcd 1:right lcd;
347 					else if (m_state == 2)
348 						m_cursor_x = ((data>>1)&0x3f) % 48 + (BIT(data,7) * 48);
349 					else if (m_state == 3)
350 					{
351 						m_cursor_y = data & 0x03;
352 						m_state = 0;
353 					}
354 
355 					m_data_bus = 0xff;
356 				}
357 				break;
358 		}
359 
360 		m_byte_count = 0;
361 		m_data_bus = 0xff;
362 	}
363 	else
364 	{
365 		switch (m_par[0] & 0x0f)
366 		{
367 			case LCD_BYTE_INPUT:
368 				{
369 					if (((m_par[0]>>5) & 0x07) != 0x03)
370 						break;
371 
372 					m_offset %= 0x180;
373 					m_data_bus = ((m_video_ram[m_bank][m_offset]<<4)&0xf0) | ((m_video_ram[m_bank][m_offset]>>4)&0x0f);
374 					m_offset++; m_byte_count++;
375 				}
376 				break;
377 			case LCD_BYTE_OUTPUT:
378 				{
379 					m_offset %= 0x180;
380 					m_video_ram[m_bank][m_offset] = compute_newval((m_par[0]>>5) & 0x07, m_video_ram[m_bank][m_offset], data);
381 					m_offset++; m_byte_count++;
382 
383 					m_data_bus = 0xff;
384 				}
385 				break;
386 			case LCD_CHAR_OUTPUT:
387 				{
388 					int char_pos = data*8;
389 
390 					for (int i=0; i<m_char_width; i++)
391 					{
392 						m_offset %= 0x180;
393 						m_video_ram[m_bank][m_offset] = compute_newval((m_par[0]>>5) & 0x07, m_video_ram[m_bank][m_offset], get_char(char_pos));
394 						m_offset++; char_pos++;
395 					}
396 
397 					m_byte_count++;
398 					m_data_bus = 0xff;
399 				}
400 				break;
401 			case LCD_CURSOR_GRAPHIC:
402 				if (m_byte_count<8)
403 				{
404 					m_cursor[m_byte_count] = data;
405 					m_byte_count++;
406 					m_data_bus = 0xff;
407 				}
408 				break;
409 			case LCD_CURSOR_CHAR:
410 				if (m_byte_count<1)
411 				{
412 					uint8_t char_code = ((data<<4)&0xf0) | ((data>>4)&0x0f);
413 
414 					for (int i=0; i<8; i++)
415 						m_cursor[i] = get_char(char_code*8 + i);
416 
417 					m_byte_count++;
418 					m_data_bus = 0xff;
419 				}
420 				break;
421 			case LCD_USER_CHARACTER:
422 				if (m_byte_count<8)
423 				{
424 					m_custom_char[(m_par[1]&0x03)][m_byte_count] = data;
425 					m_byte_count++;
426 					m_data_bus = 0xff;
427 				}
428 				break;
429 			default:
430 				m_data_bus = 0xff;
431 		}
432 
433 		m_state=0;
434 	}
435 }
436 
data_read()437 uint8_t hd44352_device::data_read()
438 {
439 	return m_data_bus;
440 }
441