1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4 
5     Hitachi HD61830 LCD Timing Controller emulation
6 
7 **********************************************************************/
8 
9 #include "emu.h"
10 #include "hd61830.h"
11 
12 #include "screen.h"
13 
14 //#define VERBOSE 1
15 #include "logmacro.h"
16 
17 
18 
19 //**************************************************************************
20 //  DEVICE DEFINITIONS
21 //**************************************************************************
22 
23 DEFINE_DEVICE_TYPE(HD61830, hd61830_device, "hd61830", "Hitachi HD61830B LCD Controller")
24 decltype(HD61830) HD61830B = HD61830;
25 
26 
27 // default address map
hd61830(address_map & map)28 void hd61830_device::hd61830(address_map &map)
29 {
30 	if (!has_configured_map(0))
31 		map(0x0000, 0xffff).ram();
32 }
33 
34 
35 // internal character generator ROM
36 ROM_START( hd61830 )
37 	ROM_REGION( 0x5c0, "hd61830", 0 ) // internal 7360-bit chargen ROM
38 	ROM_LOAD( "hd61830.bin", 0x000, 0x5c0, BAD_DUMP CRC(06a934da) SHA1(bf3f074db5dc92e6f530cb18d6c013563099a87d) ) // typed in from manual
39 ROM_END
40 
41 
42 //-------------------------------------------------
43 //  device_rom_region - device-specific ROM region
44 //-------------------------------------------------
45 
device_rom_region() const46 const tiny_rom_entry *hd61830_device::device_rom_region() const
47 {
48 	return ROM_NAME(hd61830);
49 }
50 
51 
52 
53 //**************************************************************************
54 //  MACROS / CONSTANTS
55 //**************************************************************************
56 
57 static constexpr int CYCLES[] =
58 {
59 	4, 4, 4, 4, 4, -1, -1, -1, 4, 4, 4, 4, 6, 6, 36, 36
60 };
61 
62 static constexpr int MODE_EXTERNAL_CG      = 0x01;
63 static constexpr int MODE_GRAPHIC          = 0x02;
64 static constexpr int MODE_CURSOR           = 0x04;
65 static constexpr int MODE_BLINK            = 0x08;
66 static constexpr int MODE_MASTER           = 0x10;
67 static constexpr int MODE_DISPLAY_ON       = 0x20;
68 
69 
70 
71 //**************************************************************************
72 //  LIVE DEVICE
73 //**************************************************************************
74 
75 //-------------------------------------------------
76 //  hd61830_device - constructor
77 //-------------------------------------------------
78 
hd61830_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)79 hd61830_device::hd61830_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
80 	device_t(mconfig, HD61830, tag, owner, clock),
81 	device_memory_interface(mconfig, *this),
82 	device_video_interface(mconfig, *this),
83 	m_read_rd(*this),
84 	m_bf(false),
85 	m_cac(0),
86 	m_blink(0),
87 	m_cursor(0),
88 	m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(hd61830_device::hd61830), this)),
89 	m_char_rom(*this, "hd61830")
90 {
91 }
92 
93 
94 //-------------------------------------------------
95 //  device_start - device-specific startup
96 //-------------------------------------------------
97 
device_start()98 void hd61830_device::device_start()
99 {
100 	// allocate timers
101 	m_busy_timer = timer_alloc();
102 
103 	// resolve callbacks
104 	m_read_rd.resolve_safe(0);
105 
106 	// register for state saving
107 	save_item(NAME(m_bf));
108 	save_item(NAME(m_ir));
109 	save_item(NAME(m_mcr));
110 	save_item(NAME(m_dor));
111 	save_item(NAME(m_cac));
112 	save_item(NAME(m_dsa));
113 	save_item(NAME(m_vp));
114 	save_item(NAME(m_hp));
115 	save_item(NAME(m_hn));
116 	save_item(NAME(m_nx));
117 	save_item(NAME(m_cp));
118 	save_item(NAME(m_blink));
119 	save_item(NAME(m_cursor));
120 }
121 
122 
123 //-------------------------------------------------
124 //  device_reset - device-specific reset
125 //-------------------------------------------------
126 
device_reset()127 void hd61830_device::device_reset()
128 {
129 	// display off, slave mode
130 	m_mcr &= ~(MODE_MASTER | MODE_DISPLAY_ON);
131 
132 	// default horizontal pitch
133 	m_hp = 6;
134 }
135 
136 
137 //-------------------------------------------------
138 //  device_timer - handler timer events
139 //-------------------------------------------------
140 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)141 void hd61830_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
142 {
143 	// clear busy flag
144 	m_bf = false;
145 }
146 
147 
148 //-------------------------------------------------
149 //  memory_space_config - return a description of
150 //  any address spaces owned by this device
151 //-------------------------------------------------
152 
memory_space_config() const153 device_memory_interface::space_config_vector hd61830_device::memory_space_config() const
154 {
155 	return space_config_vector {
156 		std::make_pair(0, &m_space_config)
157 	};
158 }
159 
160 
161 /*-------------------------------------------------
162     set_busy_flag - set busy flag and arm timer
163                     to clear it later
164 -------------------------------------------------*/
165 
set_busy_flag()166 void hd61830_device::set_busy_flag()
167 {
168 	// set busy flag
169 	//m_bf = true; TODO figure out correct timing
170 
171 	// adjust busy timer
172 	m_busy_timer->adjust(clocks_to_attotime(CYCLES[m_ir]));
173 }
174 
175 
176 //-------------------------------------------------
177 //  status_r - status register read
178 //-------------------------------------------------
179 
status_r()180 uint8_t hd61830_device::status_r()
181 {
182 	LOG("HD61830 Status Read: %s\n", m_bf ? "busy" : "ready");
183 
184 	return m_bf ? 0x80 : 0;
185 }
186 
187 
188 //-------------------------------------------------
189 //  control_w - instruction register write
190 //-------------------------------------------------
191 
control_w(uint8_t data)192 void hd61830_device::control_w(uint8_t data)
193 {
194 	m_ir = data;
195 }
196 
197 
198 //-------------------------------------------------
199 //  data_r - data register read
200 //-------------------------------------------------
201 
data_r()202 uint8_t hd61830_device::data_r()
203 {
204 	uint8_t data = m_dor;
205 
206 	LOG("HD61830 Display Data Read %02x\n", m_dor);
207 
208 	m_dor = readbyte(m_cac);
209 
210 	m_cac++;
211 
212 	return data;
213 }
214 
215 
216 //-------------------------------------------------
217 //  data_w - data register write
218 //-------------------------------------------------
219 
data_w(uint8_t data)220 void hd61830_device::data_w(uint8_t data)
221 {
222 	if (m_bf)
223 	{
224 		logerror("HD61830 Ignoring data write %02x due to business\n", data);
225 		return;
226 	}
227 
228 	switch (m_ir)
229 	{
230 	case INSTRUCTION_MODE_CONTROL:
231 		m_mcr = data;
232 
233 		LOG("HD61830 %s CG\n", (data & MODE_EXTERNAL_CG) ? "External" : "Internal");
234 		LOG("HD61830 %s Display Mode\n", (data & MODE_GRAPHIC) ? "Graphic" : "Character");
235 		LOG("HD61830 %s Mode\n", (data & MODE_MASTER) ? "Master" : "Slave");
236 		LOG("HD61830 Cursor %s\n", (data & MODE_CURSOR) ? "On" : "Off");
237 		LOG("HD61830 Blink %s\n", (data & MODE_BLINK) ? "On" : "Off");
238 		LOG("HD61830 Display %s\n", (data & MODE_DISPLAY_ON) ? "On" : "Off");
239 		break;
240 
241 	case INSTRUCTION_CHARACTER_PITCH:
242 		m_hp = (data & 0x07) + 1;
243 		m_vp = (data >> 4) + 1;
244 
245 		LOG("HD61830 Horizontal Character Pitch: %u\n", m_hp);
246 		LOG("HD61830 Vertical Character Pitch: %u\n", m_vp);
247 		break;
248 
249 	case INSTRUCTION_NUMBER_OF_CHARACTERS:
250 		m_hn = (data & 0x7f) + 1;
251 		m_hn = (m_hn % 2 == 0) ? m_hn : (m_hn + 1);
252 
253 		LOG("HD61830 Number of Characters: %u\n", m_hn);
254 		break;
255 
256 	case INSTRUCTION_NUMBER_OF_TIME_DIVISIONS:
257 		m_nx = (data & 0x7f) + 1;
258 
259 		LOG("HD61830 Number of Time Divisions: %u\n", m_nx);
260 		break;
261 
262 	case INSTRUCTION_CURSOR_POSITION:
263 		m_cp = (data & 0x0f) + 1;
264 
265 		LOG("HD61830 Cursor Position: %u\n", m_cp);
266 		break;
267 
268 	case INSTRUCTION_DISPLAY_START_LOW:
269 		m_dsa = (m_dsa & 0xff00) | data;
270 
271 		LOG("HD61830 Display Start Address Low %04x\n", m_dsa);
272 		break;
273 
274 	case INSTRUCTION_DISPLAY_START_HIGH:
275 		m_dsa = (data << 8) | (m_dsa & 0xff);
276 
277 		LOG("HD61830 Display Start Address High %04x\n", m_dsa);
278 		break;
279 
280 	case INSTRUCTION_CURSOR_ADDRESS_LOW:
281 		if (BIT(m_cac, 7) && !BIT(data, 7))
282 		{
283 			m_cac = (((m_cac >> 8) + 1) << 8) | data;
284 		}
285 		else
286 		{
287 			m_cac = (m_cac & 0xff00) | data;
288 		}
289 
290 		LOG("HD61830 Cursor Address Low %02x: %04x\n", data, m_cac);
291 		break;
292 
293 	case INSTRUCTION_CURSOR_ADDRESS_HIGH:
294 		m_cac = (data << 8) | (m_cac & 0xff);
295 
296 		LOG("HD61830 Cursor Address High %02x: %04x\n", data, m_cac);
297 		break;
298 
299 	case INSTRUCTION_DISPLAY_DATA_WRITE:
300 		writebyte(m_cac, data);
301 
302 		LOG("HD61830 Display Data Write %02x -> %04x row %u col %u\n", data, m_cac, m_cac / 40, m_cac % 40);
303 
304 		m_cac++;
305 		break;
306 
307 	case INSTRUCTION_CLEAR_BIT:
308 		{
309 		int bit = data & 0x07;
310 		uint8_t md = readbyte(m_cac);
311 
312 		md &= ~(1 << bit);
313 
314 		LOG("HD61830 Clear Bit %u at %04x\n", bit + 1, m_cac);
315 
316 		writebyte(m_cac, md);
317 
318 		m_cac++;
319 		}
320 		break;
321 
322 	case INSTRUCTION_SET_BIT:
323 		{
324 		int bit = data & 0x07;
325 		uint8_t md = readbyte(m_cac);
326 
327 		md |= 1 << bit;
328 
329 		LOG("HD61830 Set Bit %u at %04x\n", bit + 1, m_cac);
330 
331 		writebyte(m_cac, md);
332 
333 		m_cac++;
334 		}
335 		break;
336 
337 	default:
338 		logerror("HD61830 Illegal Instruction %02x!\n", m_ir);
339 		return;
340 	}
341 
342 	// burn cycles
343 	set_busy_flag();
344 }
345 
346 
347 //-------------------------------------------------
348 //  draw_scanline - draw one graphics scanline
349 //-------------------------------------------------
350 
draw_scanline(bitmap_ind16 & bitmap,const rectangle & cliprect,int y,uint16_t ra)351 uint16_t hd61830_device::draw_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, uint16_t ra)
352 {
353 	for (int sx = 0; sx < m_hn; sx+=2)
354 	{
355 		uint8_t data1 = readbyte(ra++);
356 		uint8_t data2 = readbyte(ra++);
357 
358 		for (int x = 0; x < m_hp; x++)
359 		{
360 			if(y >= 0 && y < bitmap.height())
361 			{
362 				if(((sx * m_hp) + x) >= 0 && ((sx * m_hp) + x) < bitmap.width())
363 					bitmap.pix(y, (sx * m_hp) + x) = BIT(data1, x);
364 				if(((sx * m_hp) + x + m_hp) >= 0 && ((sx * m_hp) + x + m_hp) < bitmap.width())
365 					bitmap.pix(y, (sx * m_hp) + x + m_hp) = BIT(data2, x);
366 			}
367 		}
368 	}
369 	return ra;
370 }
371 
372 
373 //-------------------------------------------------
374 //  update_graphics - draw graphics mode screen
375 //-------------------------------------------------
376 
update_graphics(bitmap_ind16 & bitmap,const rectangle & cliprect)377 void hd61830_device::update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect)
378 {
379 	uint16_t rac1 = m_dsa;
380 	uint16_t rac2 = rac1 + (m_nx * m_hn);
381 	for (int y = 0; y < m_nx; y++)
382 	{
383 		/* draw upper half scanline */
384 		rac1 = draw_scanline(bitmap, cliprect, y, rac1);
385 
386 		/* draw lower half scanline */
387 		rac2 = draw_scanline(bitmap, cliprect, y + m_nx, rac2);
388 	}
389 }
390 
391 
392 //-------------------------------------------------
393 //  draw_char - draw a char
394 //-------------------------------------------------
395 
draw_char(bitmap_ind16 & bitmap,const rectangle & cliprect,uint16_t ma,int x,int y,uint8_t md)396 void hd61830_device::draw_char(bitmap_ind16 &bitmap, const rectangle &cliprect, uint16_t ma, int x, int y, uint8_t md)
397 {
398 	for (int cl = 0; cl < m_vp; cl++)
399 	{
400 		for (int cr = 0; cr < m_hp; cr++)
401 		{
402 			int sy = y * m_vp + cl;
403 			int sx = x * m_hp + cr;
404 			uint8_t data;
405 
406 			if (m_mcr & MODE_EXTERNAL_CG)
407 			{
408 				data = m_read_rd((cl << 12) | md);
409 			}
410 			else
411 			{
412 				uint16_t addr = 0;
413 
414 				if (md >= 0x20 && md < 0x80 && cl < 7)
415 				{
416 					// 5x7 characters 0x20..0x7f
417 					addr = (md - 0x20) * 7 + cl;
418 				}
419 				else if (md >= 0xa0 && md < 0xe0 && cl < 7)
420 				{
421 					// 5x7 characters 0xa0..0xdf
422 					addr = 96*7 + (md - 0xa0) * 7 + cl;
423 				}
424 				else if (md >= 0xe0 && cl < 11)
425 				{
426 					// 5x11 characters 0xe0..0xff
427 					addr = 160*7 + (md - 0xe0) * 11 + cl;
428 				}
429 
430 				data = m_char_rom[addr];
431 			}
432 
433 			int cursor = m_mcr & MODE_CURSOR;
434 			int blink = m_mcr & MODE_BLINK;
435 
436 			// cursor off
437 			int pixel = BIT(data, cr);
438 
439 			if (blink && (ma == m_cac))
440 			{
441 				// cursor off, character blink
442 				if (!cursor)
443 					pixel = m_cursor ? pixel : 0;
444 
445 				// cursor blink
446 				if (cursor && (cl == m_cp))
447 					pixel = m_cursor ? 1 : 0;
448 			}
449 			else
450 			{
451 				// cursor on
452 				if (cursor && (cl == m_cp))
453 					pixel = m_cursor ? 1 : 0;
454 			}
455 
456 			if (sy < screen().height() && sx < screen().width())
457 				bitmap.pix(sy, sx) = pixel;
458 		}
459 	}
460 }
461 
462 
463 //-------------------------------------------------
464 //  update_text - draw text mode screen
465 //-------------------------------------------------
466 
update_text(bitmap_ind16 & bitmap,const rectangle & cliprect)467 void hd61830_device::update_text(bitmap_ind16 &bitmap, const rectangle &cliprect)
468 {
469 	uint16_t ma = 0;
470 	int rows = m_nx / m_vp;
471 	uint16_t rac1 = m_dsa & 0xfff;
472 	uint16_t rac2 = rac1 + (rows * m_hn);
473 	for (int y = 0; y < rows; y++)
474 	{
475 		for (int x = 0; x < m_hn; x+=2)
476 		{
477 			uint8_t md1 = readbyte(rac1);
478 			uint8_t md2 = readbyte(rac1+1);
479 
480 			draw_char(bitmap, cliprect, ma, x, y, md1);
481 			draw_char(bitmap, cliprect, ma+1, x+1, y, md2);
482 
483 			md1 = readbyte(rac2);
484 			md2 = readbyte(rac2+1);
485 
486 			draw_char(bitmap, cliprect, ma, x, y + rows, md1);
487 			draw_char(bitmap, cliprect, ma+1, x+1, y + rows, md2);
488 
489 			ma+=2;
490 			rac1+=2;
491 			rac2+=2;
492 		}
493 	}
494 }
495 
496 
497 //-------------------------------------------------
498 //  update_screen - update screen
499 //-------------------------------------------------
500 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)501 uint32_t hd61830_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
502 {
503 	if (m_mcr & MODE_DISPLAY_ON)
504 	{
505 		if (m_mcr & MODE_GRAPHIC)
506 		{
507 			update_graphics(bitmap, cliprect);
508 		}
509 		else
510 		{
511 			update_text(bitmap, cliprect);
512 		}
513 	}
514 	else
515 	{
516 		bitmap.fill(0, cliprect);
517 	}
518 
519 	m_blink++;
520 
521 	if (m_blink == 0x20)
522 	{
523 		m_blink = 0;
524 		m_cursor = !m_cursor;
525 	}
526 	return 0;
527 }
528