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