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