1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4
5 Seiko-Epson SED1330 LCD Controller emulation
6
7 **********************************************************************/
8
9 #include "emu.h"
10 #include "sed1330.h"
11 #include "screen.h"
12
13 //#define VERBOSE 1
14 #include "logmacro.h"
15
16
17
18 //**************************************************************************
19 // MACROS / CONSTANTS
20 //**************************************************************************
21
22
23 #define INSTRUCTION_SYSTEM_SET 0x40
24 #define INSTRUCTION_SLEEP_IN 0x53 // unimplemented
25 #define INSTRUCTION_DISP_ON 0x59
26 #define INSTRUCTION_DISP_OFF 0x58
27 #define INSTRUCTION_SCROLL 0x44
28 #define INSTRUCTION_CSRFORM 0x5d
29 #define INSTRUCTION_CGRAM_ADR 0x5c
30 #define INSTRUCTION_CSRDIR_RIGHT 0x4c
31 #define INSTRUCTION_CSRDIR_LEFT 0x4d
32 #define INSTRUCTION_CSRDIR_UP 0x4e
33 #define INSTRUCTION_CSRDIR_DOWN 0x4f
34 #define INSTRUCTION_HDOT_SCR 0x5a
35 #define INSTRUCTION_OVLAY 0x5b
36 #define INSTRUCTION_CSRW 0x46
37 #define INSTRUCTION_CSRR 0x47
38 #define INSTRUCTION_MWRITE 0x42
39 #define INSTRUCTION_MREAD 0x43
40
41
42 #define CSRDIR_RIGHT 0x00
43 #define CSRDIR_LEFT 0x01
44 #define CSRDIR_UP 0x02
45 #define CSRDIR_DOWN 0x03
46
47
48 #define MX_OR 0x00
49 #define MX_XOR 0x01 // unimplemented
50 #define MX_AND 0x02 // unimplemented
51 #define MX_PRIORITY_OR 0x03 // unimplemented
52
53
54 #define FC_OFF 0x00
55 #define FC_SOLID 0x01
56 #define FC_FLASH_32 0x02 // unimplemented
57 #define FC_FLASH_64 0x03 // unimplemented
58
59
60 #define FP_OFF 0x00
61 #define FP_SOLID 0x01
62 #define FP_FLASH_32 0x02 // unimplemented
63 #define FP_FLASH_4 0x03 // unimplemented
64
65
66
67 //**************************************************************************
68 // GLOBAL VARIABLES
69 //**************************************************************************
70
71 // device type definition
72 DEFINE_DEVICE_TYPE(SED1330, sed1330_device, "sed1330", "Epson SED1330")
73
74
75 // default address map
sed1330(address_map & map)76 void sed1330_device::sed1330(address_map &map)
77 {
78 if (!has_configured_map(0))
79 map(0x0000, 0xffff).ram();
80 }
81
82
83 // internal character generator ROM
84 ROM_START( sed1330 )
85 ROM_REGION( 0x5c0, "gfx1", 0 ) // internal chargen ROM
86 ROM_LOAD( "sed1330.bin", 0x000, 0x5c0, NO_DUMP )
87 ROM_END
88
89
90
91 //**************************************************************************
92 // INLINE HELPERS
93 //**************************************************************************
94
95 //-------------------------------------------------
96 // readbyte - read a byte at the given address
97 //-------------------------------------------------
98
readbyte(offs_t address)99 inline uint8_t sed1330_device::readbyte(offs_t address)
100 {
101 return space().read_byte(address);
102 }
103
104
105 //-------------------------------------------------
106 // writebyte - write a byte at the given address
107 //-------------------------------------------------
108
writebyte(offs_t address,uint8_t data)109 inline void sed1330_device::writebyte(offs_t address, uint8_t data)
110 {
111 space().write_byte(address, data);
112 }
113
114
115 //-------------------------------------------------
116 // increment_csr - increment cursor address
117 //-------------------------------------------------
118
increment_csr()119 inline void sed1330_device::increment_csr()
120 {
121 switch (m_cd)
122 {
123 case CSRDIR_RIGHT:
124 m_csr++;
125 break;
126
127 case CSRDIR_LEFT:
128 m_csr--;
129 break;
130
131 case CSRDIR_UP:
132 m_csr -= m_ap;
133 break;
134
135 case CSRDIR_DOWN:
136 m_csr += m_ap;
137 break;
138 }
139 }
140
141
142
143 //**************************************************************************
144 // LIVE DEVICE
145 //**************************************************************************
146
147 //-------------------------------------------------
148 // sed1330_device - constructor
149 //-------------------------------------------------
150
sed1330_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)151 sed1330_device::sed1330_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
152 : device_t(mconfig, SED1330, tag, owner, clock),
153 device_memory_interface(mconfig, *this),
154 device_video_interface(mconfig, *this),
155 m_bf(0),
156 m_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(sed1330_device::sed1330), this))
157 {
158 }
159
160
161 //-------------------------------------------------
162 // rom_region - device-specific ROM region
163 //-------------------------------------------------
164
device_rom_region() const165 const tiny_rom_entry *sed1330_device::device_rom_region() const
166 {
167 return ROM_NAME( sed1330 );
168 }
169
170
171 //-------------------------------------------------
172 // device_start - device-specific startup
173 //-------------------------------------------------
174
device_start()175 void sed1330_device::device_start()
176 {
177 space().cache(m_cache);
178
179 // register for state saving
180 save_item(NAME(m_bf));
181 save_item(NAME(m_ir));
182 save_item(NAME(m_dor));
183 save_item(NAME(m_pbc));
184 save_item(NAME(m_d));
185 save_item(NAME(m_sleep));
186 save_item(NAME(m_sag));
187 save_item(NAME(m_m0));
188 save_item(NAME(m_m1));
189 save_item(NAME(m_m2));
190 save_item(NAME(m_ws));
191 save_item(NAME(m_iv));
192 save_item(NAME(m_wf));
193 save_item(NAME(m_fx));
194 save_item(NAME(m_fy));
195 save_item(NAME(m_cr));
196 save_item(NAME(m_tcr));
197 save_item(NAME(m_lf));
198 save_item(NAME(m_ap));
199 save_item(NAME(m_sad1));
200 save_item(NAME(m_sad2));
201 save_item(NAME(m_sad3));
202 save_item(NAME(m_sad4));
203 save_item(NAME(m_sl1));
204 save_item(NAME(m_sl2));
205 save_item(NAME(m_hdotscr));
206 save_item(NAME(m_csr));
207 save_item(NAME(m_cd));
208 save_item(NAME(m_crx));
209 save_item(NAME(m_cry));
210 save_item(NAME(m_cm));
211 save_item(NAME(m_fc));
212 save_item(NAME(m_fp));
213 save_item(NAME(m_mx));
214 save_item(NAME(m_dm));
215 save_item(NAME(m_ov));
216 }
217
218
219 //-------------------------------------------------
220 // device_reset - device-specific reset
221 //-------------------------------------------------
222
device_reset()223 void sed1330_device::device_reset()
224 {
225 }
226
227
228 //-------------------------------------------------
229 // memory_space_config - return a description of
230 // any address spaces owned by this device
231 //-------------------------------------------------
232
memory_space_config() const233 device_memory_interface::space_config_vector sed1330_device::memory_space_config() const
234 {
235 return space_config_vector {
236 std::make_pair(0, &m_space_config)
237 };
238 }
239
240
241 //-------------------------------------------------
242 // status_r -
243 //-------------------------------------------------
244
status_r()245 uint8_t sed1330_device::status_r()
246 {
247 if (!machine().side_effects_disabled())
248 LOG("SED1330 Status Read: %s\n", m_bf ? "busy" : "ready");
249
250 return m_bf << 6;
251 }
252
253
254 //-------------------------------------------------
255 // command_w -
256 //-------------------------------------------------
257
command_w(uint8_t data)258 void sed1330_device::command_w(uint8_t data)
259 {
260 m_ir = data;
261 m_pbc = 0;
262
263 switch (m_ir)
264 {
265 #if 0
266 case INSTRUCTION_SLEEP_IN:
267 break;
268 #endif
269 case INSTRUCTION_CSRDIR_RIGHT:
270 case INSTRUCTION_CSRDIR_LEFT:
271 case INSTRUCTION_CSRDIR_UP:
272 case INSTRUCTION_CSRDIR_DOWN:
273 m_cd = data & 0x03;
274
275 switch (m_cd)
276 {
277 case CSRDIR_RIGHT: LOG("SED1330 Cursor Direction: Right\n"); break;
278 case CSRDIR_LEFT: LOG("SED1330 Cursor Direction: Left\n"); break;
279 case CSRDIR_UP: LOG("SED1330 Cursor Direction: Up\n"); break;
280 case CSRDIR_DOWN: LOG("SED1330 Cursor Direction: Down\n"); break;
281 }
282 break;
283 }
284 }
285
286
287 //-------------------------------------------------
288 // data_r -
289 //-------------------------------------------------
290
data_r()291 uint8_t sed1330_device::data_r()
292 {
293 uint8_t data = 0;
294
295 switch (m_ir)
296 {
297 case INSTRUCTION_MREAD:
298 data = readbyte(m_csr);
299 if (!machine().side_effects_disabled())
300 {
301 LOG("SED1330 Memory Read %02x from %04x\n", data, m_csr);
302 increment_csr();
303 }
304 break;
305
306 case INSTRUCTION_CSRR:
307 switch (m_pbc)
308 {
309 case 0:
310 data = m_csr & 0xff;
311 break;
312
313 case 1:
314 data = (m_csr & 0xff00) >> 8;
315 break;
316
317 default:
318 logerror("SED1330 Invalid parameter byte %02x\n", data);
319 }
320 if (!machine().side_effects_disabled())
321 {
322 LOG("SED1330 Cursor Byte %d Read %02x\n", m_pbc, data);
323 m_pbc++;
324 }
325 break;
326
327 default:
328 logerror("SED1330 Unsupported instruction %02x\n", m_ir);
329 break;
330 }
331
332 return data;
333 }
334
335
336 //-------------------------------------------------
337 // data_w -
338 //-------------------------------------------------
339
data_w(uint8_t data)340 void sed1330_device::data_w(uint8_t data)
341 {
342 switch (m_ir)
343 {
344 case INSTRUCTION_SYSTEM_SET:
345 switch (m_pbc)
346 {
347 case 0:
348 m_m0 = BIT(data, 0);
349 m_m1 = BIT(data, 1);
350 m_m2 = BIT(data, 2);
351 m_ws = BIT(data, 3);
352 m_iv = BIT(data, 5);
353
354 LOG("SED1330 %s CG ROM\n", BIT(data, 0) ? "External" : "Internal");
355 LOG("SED1330 D6 Correction: %s\n", BIT(data, 1) ? "enabled" : "disabled");
356 LOG("SED1330 Character Height: %u\n", BIT(data, 2) ? 16 : 8);
357 LOG("SED1330 %s Panel Drive\n", BIT(data, 3) ? "Dual" : "Single");
358 LOG("SED1330 Screen Top-Line Correction: %s\n", BIT(data, 5) ? "disabled" : "enabled");
359 break;
360
361 case 1:
362 m_fx = (data & 0x07) + 1;
363 m_wf = BIT(data, 7);
364
365 LOG("SED1330 Horizontal Character Size: %u\n", m_fx);
366 LOG("SED1330 %s AC Drive\n", BIT(data, 7) ? "2-frame" : "16-line");
367 break;
368
369 case 2:
370 m_fy = (data & 0x0f) + 1;
371 LOG("SED1330 Vertical Character Size: %u\n", m_fy);
372 break;
373
374 case 3:
375 m_cr = data + 1;
376 LOG("SED1330 Visible Characters Per Line: %u\n", m_cr);
377 break;
378
379 case 4:
380 m_tcr = data + 1;
381 LOG("SED1330 Total Characters Per Line: %u\n", m_tcr);
382 break;
383
384 case 5:
385 m_lf = data + 1;
386 LOG("SED1330 Frame Height: %u\n", m_lf);
387 if (clock() != 0)
388 {
389 attotime fr = clocks_to_attotime(m_tcr * m_lf * 9);
390 screen().configure(m_tcr * m_fx, m_lf, screen().visible_area(), fr.as_attoseconds());
391 LOG("SED1330 Frame Rate: %.1f Hz\n", fr.as_hz());
392 }
393 break;
394
395 case 6:
396 m_ap = (m_ap & 0xff00) | data;
397 break;
398
399 case 7:
400 m_ap = (data << 8) | (m_ap & 0xff);
401 LOG("SED1330 Virtual Screen Width: %u\n", m_ap);
402 break;
403
404 default:
405 logerror("SED1330 Invalid parameter byte %02x\n", data);
406 }
407 break;
408
409 case INSTRUCTION_DISP_ON:
410 case INSTRUCTION_DISP_OFF:
411 m_d = BIT(m_ir, 0);
412 m_fc = data & 0x03;
413 m_fp = data >> 2;
414 LOG("SED1330 Display: %s\n", BIT(m_ir, 0) ? "enabled" : "disabled");
415
416 switch (m_fc)
417 {
418 case FC_OFF: LOG("SED1330 Cursor: disabled\n"); break;
419 case FC_SOLID: LOG("SED1330 Cursor: solid\n"); break;
420 case FC_FLASH_32: LOG("SED1330 Cursor: fFR/32\n"); break;
421 case FC_FLASH_64: LOG("SED1330 Cursor: fFR/64\n"); break;
422 }
423
424 switch (m_fp & 0x03)
425 {
426 case FP_OFF: LOG("SED1330 Display Page 1: disabled\n"); break;
427 case FP_SOLID: LOG("SED1330 Display Page 1: enabled\n"); break;
428 case FP_FLASH_32: LOG("SED1330 Display Page 1: flash fFR/32\n"); break;
429 case FP_FLASH_4: LOG("SED1330 Display Page 1: flash fFR/4\n"); break;
430 }
431
432 switch ((m_fp >> 2) & 0x03)
433 {
434 case FP_OFF: LOG("SED1330 Display Page 2/4: disabled\n"); break;
435 case FP_SOLID: LOG("SED1330 Display Page 2/4: enabled\n"); break;
436 case FP_FLASH_32: LOG("SED1330 Display Page 2/4: flash fFR/32\n"); break;
437 case FP_FLASH_4: LOG("SED1330 Display Page 2/4: flash fFR/4\n"); break;
438 }
439
440 switch ((m_fp >> 4) & 0x03)
441 {
442 case FP_OFF: LOG("SED1330 Display Page 3: disabled\n"); break;
443 case FP_SOLID: LOG("SED1330 Display Page 3: enabled\n"); break;
444 case FP_FLASH_32: LOG("SED1330 Display Page 3: flash fFR/32\n"); break;
445 case FP_FLASH_4: LOG("SED1330 Display Page 3: flash fFR/4\n"); break;
446 }
447 break;
448
449 case INSTRUCTION_SCROLL:
450 switch (m_pbc)
451 {
452 case 0:
453 m_sad1 = (m_sad1 & 0xff00) | data;
454 break;
455
456 case 1:
457 m_sad1 = (data << 8) | (m_sad1 & 0xff);
458 LOG("SED1330 Display Page 1 Start Address: %04x\n", m_sad1);
459 break;
460
461 case 2:
462 m_sl1 = data + 1;
463 LOG("SED1330 Display Block 1 Screen Lines: %u\n", m_sl1);
464 break;
465
466 case 3:
467 m_sad2 = (m_sad2 & 0xff00) | data;
468 break;
469
470 case 4:
471 m_sad2 = (data << 8) | (m_sad2 & 0xff);
472 LOG("SED1330 Display Page 2 Start Address: %04x\n", m_sad2);
473 break;
474
475 case 5:
476 m_sl2 = data + 1;
477 LOG("SED1330 Display Block 2 Screen Lines: %u\n", m_sl2);
478 break;
479
480 case 6:
481 m_sad3 = (m_sad3 & 0xff00) | data;
482 break;
483
484 case 7:
485 m_sad3 = (data << 8) | (m_sad3 & 0xff);
486 LOG("SED1330 Display Page 3 Start Address: %04x\n", m_sad3);
487 break;
488
489 case 8:
490 m_sad4 = (m_sad4 & 0xff00) | data;
491 break;
492
493 case 9:
494 m_sad4 = (data << 8) | (m_sad4 & 0xff);
495 LOG("SED1330 Display Page 4 Start Address: %04x\n", m_sad4);
496 break;
497
498 default:
499 logerror("SED1330 Invalid parameter byte %02x\n", data);
500 }
501 break;
502
503 case INSTRUCTION_CSRFORM:
504 switch (m_pbc)
505 {
506 case 0:
507 m_crx = (data & 0x0f) + 1;
508 LOG("SED1330 Horizontal Cursor Size: %u\n", m_crx);
509 break;
510
511 case 1:
512 m_cry = (data & 0x0f) + 1;
513 m_cm = BIT(data, 7);
514 LOG("SED1330 Vertical Cursor Location: %u\n", m_cry);
515 LOG("SED1330 Cursor Shape: %s\n", BIT(data, 7) ? "Block" : "Underscore");
516 break;
517
518 default:
519 logerror("SED1330 Invalid parameter byte %02x\n", data);
520 }
521 break;
522
523 case INSTRUCTION_CGRAM_ADR:
524 switch (m_pbc)
525 {
526 case 0:
527 m_sag = (m_sag & 0xff00) | data;
528 break;
529
530 case 1:
531 m_sag = (data << 8) | (m_sag & 0xff);
532 LOG("SED1330 Character Generator RAM Start Address: %04x\n", m_sag);
533 break;
534
535 default:
536 logerror("SED1330 Invalid parameter byte %02x\n", data);
537 }
538 break;
539
540 case INSTRUCTION_HDOT_SCR:
541 m_hdotscr = data & 0x07;
542 LOG("SED1330 Horizontal Dot Scroll: %u\n", m_hdotscr);
543 break;
544
545 case INSTRUCTION_OVLAY:
546 m_mx = data & 0x03;
547 m_dm = (data >> 2) & 0x03;
548 m_ov = BIT(data, 4);
549
550 switch (m_mx)
551 {
552 case MX_OR: LOG("SED1330 Display Composition Method: OR\n"); break;
553 case MX_XOR: LOG("SED1330 Display Composition Method: Exclusive-OR\n"); break;
554 case MX_AND: LOG("SED1330 Display Composition Method: AND\n"); break;
555 case MX_PRIORITY_OR: LOG("SED1330 Display Composition Method: Priority-OR\n"); break;
556 }
557
558 LOG("SED1330 Display Page 1 Mode: %s\n", BIT(data, 2) ? "Graphics" : "Text");
559 LOG("SED1330 Display Page 3 Mode: %s\n", BIT(data, 3) ? "Graphics" : "Text");
560 LOG("SED1330 Display Composition Layers: %u\n", BIT(data, 4) ? 3 : 2);
561 break;
562
563 case INSTRUCTION_CSRW:
564 switch (m_pbc)
565 {
566 case 0:
567 m_csr = (m_csr & 0xff00) | data;
568 break;
569
570 case 1:
571 m_csr = (data << 8) | (m_csr & 0xff);
572 LOG("SED1330 Cursor Address %04x\n", m_csr);
573 break;
574
575 default:
576 logerror("SED1330 Invalid parameter byte %02x\n", data);
577 }
578 break;
579 #if 0
580 case INSTRUCTION_CSRR:
581 break;
582 #endif
583 case INSTRUCTION_MWRITE:
584 LOG("SED1330 Memory Write %02x to %04x (row %u col %u line %u)\n", data, m_csr, m_csr/80/8, m_csr%80, m_csr/80);
585
586 writebyte(m_csr, data);
587
588 increment_csr();
589 break;
590 #if 0
591 case INSTRUCTION_MREAD:
592 break;
593 #endif
594 default:
595 logerror("SED1330 Unsupported instruction %02x\n", m_ir);
596 }
597
598 m_pbc++;
599 }
600
601
602 //-------------------------------------------------
603 // draw_text_scanline -
604 //-------------------------------------------------
605
draw_text_scanline(bitmap_ind16 & bitmap,const rectangle & cliprect,int y,int r,uint16_t va,bool cursor)606 void sed1330_device::draw_text_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, int r, uint16_t va, bool cursor)
607 {
608 uint16_t *p = &bitmap.pix(y);
609
610 for (int sx = 0; sx < m_cr; sx++, p += m_fx)
611 {
612 if (m_m0 && !m_m1)
613 {
614 uint8_t c = m_cache.read_byte(va + sx);
615 uint8_t data = m_cache.read_byte(0xf000 | (m_m2 ? u16(c) << 4 | r : u16(c) << 3 | (r & 7)));
616 for (int x = 0; x < m_fx; x++, data <<= 1)
617 if (BIT(data, 7))
618 p[x] = 1;
619 }
620
621 if (cursor && (va + sx) == m_csr)
622 {
623 if (m_cm)
624 {
625 // block cursor
626 if (r < m_cry)
627 {
628 std::fill_n(p, m_crx, 1);
629 }
630 }
631 else
632 {
633 // underscore cursor
634 if (r == m_cry)
635 {
636 std::fill_n(p, m_crx, 1);
637 }
638 }
639 }
640 }
641 }
642
643
644 //-------------------------------------------------
645 // draw_graphics_scanline -
646 //-------------------------------------------------
647
draw_graphics_scanline(bitmap_ind16 & bitmap,const rectangle & cliprect,int y,uint16_t va)648 void sed1330_device::draw_graphics_scanline(bitmap_ind16 &bitmap, const rectangle &cliprect, int y, uint16_t va)
649 {
650 for (int sx = 0; sx < m_cr; sx++)
651 {
652 uint8_t data = readbyte(va++);
653
654 for (int x = 0; x < m_fx; x++)
655 {
656 bitmap.pix(y, (sx * m_fx) + x) = BIT(data, 7);
657 data <<= 1;
658 }
659 }
660 }
661
662
663 //-------------------------------------------------
664 // update_graphics -
665 //-------------------------------------------------
666
update_graphics(bitmap_ind16 & bitmap,const rectangle & cliprect)667 void sed1330_device::update_graphics(bitmap_ind16 &bitmap, const rectangle &cliprect)
668 {
669 }
670
671
672 //-------------------------------------------------
673 // update_text -
674 //-------------------------------------------------
675
update_text(bitmap_ind16 & bitmap,const rectangle & cliprect)676 void sed1330_device::update_text(bitmap_ind16 &bitmap, const rectangle &cliprect)
677 {
678 uint8_t attr1 = m_fp & 0x03;
679 uint8_t attr2 = (m_fp >> 2) & 0x03;
680 uint8_t attr3 = (m_fp >> 4) & 0x03;
681
682 for (int y = 0; y < m_lf; y++)
683 {
684 if (y >= m_sl1)
685 {
686 if (attr3 != FP_OFF)
687 {
688 uint16_t sad3 = m_sad3 + (((y - m_sl1) / m_fy) * m_ap);
689
690 // draw text display page 3 scanline
691 draw_text_scanline(bitmap, cliprect, y, (y - m_sl1) % m_fy, sad3, m_ov && m_fc != FC_OFF);
692 }
693 }
694 else
695 {
696 if (attr1 != FP_OFF)
697 {
698 uint16_t sad1 = m_sad1 + ((y / m_fy) * m_ap);
699
700 // draw text display page 1 scanline
701 draw_text_scanline(bitmap, cliprect, y, y % m_fy, sad1, !m_ov && m_fc != FC_OFF);
702 }
703 }
704
705 if (attr2 != FP_OFF)
706 {
707 if (m_ws && y >= m_sl2)
708 {
709 uint16_t sad4 = m_sad4 + ((y - m_sl2) * m_ap);
710
711 // draw graphics display page 4 scanline
712 draw_graphics_scanline(bitmap, cliprect, y, sad4);
713 }
714 else
715 {
716 uint16_t sad2 = m_sad2 + (y * m_ap);
717
718 // draw graphics display page 2 scanline
719 draw_graphics_scanline(bitmap, cliprect, y, sad2);
720 }
721 }
722 }
723 }
724
725
726 //-------------------------------------------------
727 // screen_update -
728 //-------------------------------------------------
729
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)730 uint32_t sed1330_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
731 {
732 bitmap.fill(0, cliprect);
733 if (m_d)
734 {
735 if (m_dm)
736 {
737 update_graphics(bitmap, cliprect);
738 }
739 else
740 {
741 update_text(bitmap, cliprect);
742 }
743 }
744 return 0;
745 }
746