diff options
Diffstat (limited to 'firmware/drivers/lcd-charcell.c')
-rw-r--r-- | firmware/drivers/lcd-charcell.c | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/firmware/drivers/lcd-charcell.c b/firmware/drivers/lcd-charcell.c new file mode 100644 index 0000000000..ce0eca94ca --- /dev/null +++ b/firmware/drivers/lcd-charcell.c | |||
@@ -0,0 +1,612 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Jens Arnold | ||
11 | * Based on the work of Alan Korr, Kjell Ericson and others | ||
12 | * | ||
13 | * All files in this archive are subject to the GNU General Public License. | ||
14 | * See the file COPYING in the source tree root for full license agreement. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | #include "config.h" | ||
21 | #include "hwcompat.h" | ||
22 | |||
23 | #include "lcd.h" | ||
24 | #include "kernel.h" | ||
25 | #include "thread.h" | ||
26 | #include <string.h> | ||
27 | #include <stdlib.h> | ||
28 | #include "file.h" | ||
29 | #include "debug.h" | ||
30 | #include "system.h" | ||
31 | #include "lcd-charcell.h" | ||
32 | #include "rbunicode.h" | ||
33 | |||
34 | /** definitions **/ | ||
35 | |||
36 | #define SCROLLABLE_LINES LCD_HEIGHT | ||
37 | #define VARIABLE_XCHARS 16 /* number of software user-definable characters */ | ||
38 | |||
39 | #define NO_PATTERN (-1) | ||
40 | |||
41 | #define SCROLL_MODE_OFF 0 | ||
42 | #define SCROLL_MODE_RUN 1 | ||
43 | |||
44 | /* track usage of user-definable characters */ | ||
45 | struct pattern_info { | ||
46 | short count; | ||
47 | unsigned short xchar; | ||
48 | }; | ||
49 | |||
50 | struct cursor_info { | ||
51 | unsigned char hw_char; | ||
52 | bool enabled; | ||
53 | bool visible; | ||
54 | int x; | ||
55 | int y; | ||
56 | int divider; | ||
57 | int downcount; | ||
58 | }; | ||
59 | |||
60 | static int find_xchar(unsigned long ucs); | ||
61 | |||
62 | /** globals **/ | ||
63 | |||
64 | /* The "frame"buffer */ | ||
65 | static unsigned char lcd_buffer[LCD_WIDTH][LCD_HEIGHT]; | ||
66 | #ifdef SIMULATOR | ||
67 | unsigned char hardware_buffer_lcd[LCD_WIDTH][LCD_HEIGHT]; | ||
68 | #endif | ||
69 | |||
70 | static int xmargin = 0; | ||
71 | static int ymargin = 0; | ||
72 | |||
73 | static unsigned char xfont_variable[VARIABLE_XCHARS][(HW_PATTERN_SIZE+3)&~3]; | ||
74 | /* round up pattern size to a multiple of 4 bytes for faster access */ | ||
75 | static bool xfont_variable_locked[VARIABLE_XCHARS]; | ||
76 | static struct pattern_info hw_pattern[MAX_HW_PATTERNS]; | ||
77 | static struct cursor_info cursor; | ||
78 | |||
79 | /* scrolling */ | ||
80 | static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */ | ||
81 | static void scroll_thread(void); | ||
82 | static char scroll_stack[DEFAULT_STACK_SIZE]; | ||
83 | static const char scroll_name[] = "scroll"; | ||
84 | static int scroll_ticks = 12; /* # of ticks between updates */ | ||
85 | static int scroll_delay = HZ/2; /* delay before starting scroll */ | ||
86 | static int bidir_limit = 50; /* percent */ | ||
87 | static int jump_scroll_delay = HZ/4; /* delay between jump scroll jumps */ | ||
88 | static int jump_scroll = 0; /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */ | ||
89 | static struct scrollinfo scroll[SCROLLABLE_LINES]; | ||
90 | |||
91 | static const char scroll_tick_table[16] = { | ||
92 | /* Hz values: | ||
93 | 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */ | ||
94 | 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3 | ||
95 | }; | ||
96 | |||
97 | /* LCD init */ | ||
98 | void lcd_init (void) | ||
99 | { | ||
100 | lcd_init_device(); | ||
101 | lcd_charset_init(); | ||
102 | memset(hw_pattern, 0, sizeof(hw_pattern)); | ||
103 | memset(lcd_buffer, xchar_info[find_xchar(' ')].hw_char, sizeof(lcd_buffer)); | ||
104 | |||
105 | create_thread(scroll_thread, scroll_stack, | ||
106 | sizeof(scroll_stack), scroll_name | ||
107 | IF_PRIO(, PRIORITY_USER_INTERFACE) | ||
108 | IF_COP(, CPU, false)); | ||
109 | } | ||
110 | |||
111 | /** parameter handling **/ | ||
112 | |||
113 | void lcd_setmargins(int x, int y) | ||
114 | { | ||
115 | xmargin = x; | ||
116 | ymargin = y; | ||
117 | } | ||
118 | |||
119 | int lcd_getxmargin(void) | ||
120 | { | ||
121 | return xmargin; | ||
122 | } | ||
123 | |||
124 | int lcd_getymargin(void) | ||
125 | { | ||
126 | return ymargin; | ||
127 | } | ||
128 | |||
129 | int lcd_getstringsize(const unsigned char *str, int *w, int *h) | ||
130 | { | ||
131 | int width = utf8length(str); | ||
132 | |||
133 | if (w) | ||
134 | *w = width; | ||
135 | if (h) | ||
136 | *h = 1; | ||
137 | |||
138 | return width; | ||
139 | } | ||
140 | |||
141 | /** low-level functions **/ | ||
142 | |||
143 | static int find_xchar(unsigned long ucs) | ||
144 | { | ||
145 | int low = 0; | ||
146 | int high = xchar_info_size - 1; | ||
147 | |||
148 | do | ||
149 | { | ||
150 | int probe = (low + high) >> 1; | ||
151 | |||
152 | if (xchar_info[probe].ucs < ucs) | ||
153 | low = probe + 1; | ||
154 | else if (xchar_info[probe].ucs > ucs) | ||
155 | high = probe - 1; | ||
156 | else | ||
157 | return probe; | ||
158 | } | ||
159 | while (low <= high); | ||
160 | |||
161 | /* Not found: return index of no-char symbol (last symbol, hardcoded). */ | ||
162 | return xchar_info_size - 1; | ||
163 | } | ||
164 | |||
165 | static int xchar_to_pat(int xchar) | ||
166 | { | ||
167 | int i; | ||
168 | |||
169 | for (i = 0; i < hw_pattern_count; i++) | ||
170 | if (hw_pattern[i].xchar == xchar) | ||
171 | return i; | ||
172 | |||
173 | return NO_PATTERN; | ||
174 | } | ||
175 | |||
176 | static const unsigned char *xchar_to_glyph(int xchar) | ||
177 | { | ||
178 | unsigned index = xchar_info[xchar].glyph; | ||
179 | |||
180 | if (index & 0x8000) | ||
181 | return xfont_variable[index & 0x7fff]; | ||
182 | else | ||
183 | return xfont_fixed[index]; | ||
184 | } | ||
185 | |||
186 | static void lcd_free_pat(int xchar) | ||
187 | { | ||
188 | int x, y; | ||
189 | unsigned char substitute; | ||
190 | int pat = xchar_to_pat(xchar); | ||
191 | |||
192 | if (pat != NO_PATTERN) | ||
193 | { | ||
194 | substitute = xchar_info[xchar].hw_char; | ||
195 | |||
196 | for (x = 0; x < LCD_WIDTH; x++) | ||
197 | { | ||
198 | for (y = 0; y < LCD_HEIGHT; y++) | ||
199 | { | ||
200 | if (pat == lcd_buffer[x][y]) | ||
201 | { | ||
202 | lcd_buffer[x][y] = substitute; | ||
203 | #ifdef SIMULATOR | ||
204 | hardware_buffer_lcd[x][y] = substitute; | ||
205 | #else | ||
206 | lcd_put_hw_char(x, y, substitute); | ||
207 | #endif | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | if (cursor.enabled && pat == cursor.hw_char) | ||
212 | cursor.hw_char = substitute; | ||
213 | |||
214 | hw_pattern[pat].count = 0; | ||
215 | #ifdef SIMULATOR | ||
216 | lcd_update(); | ||
217 | #endif | ||
218 | } | ||
219 | } | ||
220 | |||
221 | static int lcd_get_free_pat(int xchar) | ||
222 | { | ||
223 | static int last_used_pat = 0; | ||
224 | |||
225 | int pat = last_used_pat; /* start from last used pattern */ | ||
226 | int least_pat = pat; /* pattern with least priority */ | ||
227 | int least_priority = xchar_info[hw_pattern[pat].xchar].priority; | ||
228 | int i; | ||
229 | |||
230 | for (i = 0; i < hw_pattern_count; i++) | ||
231 | { | ||
232 | if (++pat >= hw_pattern_count) /* Keep 'pat' within limits */ | ||
233 | pat = 0; | ||
234 | |||
235 | if (hw_pattern[pat].count == 0) | ||
236 | { | ||
237 | hw_pattern[pat].xchar = xchar; | ||
238 | last_used_pat = pat; | ||
239 | return pat; | ||
240 | } | ||
241 | if (xchar_info[hw_pattern[pat].xchar].priority < least_priority) | ||
242 | { | ||
243 | least_priority = xchar_info[hw_pattern[pat].xchar].priority; | ||
244 | least_pat = pat; | ||
245 | } | ||
246 | } | ||
247 | if (xchar_info[xchar].priority > least_priority) /* prioritized char */ | ||
248 | { | ||
249 | lcd_free_pat(hw_pattern[least_pat].xchar); | ||
250 | hw_pattern[least_pat].xchar = xchar; | ||
251 | last_used_pat = least_pat; | ||
252 | return least_pat; | ||
253 | } | ||
254 | return NO_PATTERN; | ||
255 | } | ||
256 | |||
257 | static int map_xchar(int xchar) | ||
258 | { | ||
259 | int pat; | ||
260 | |||
261 | if (xchar_info[xchar].priority > 0) /* soft char */ | ||
262 | { | ||
263 | pat = xchar_to_pat(xchar); | ||
264 | |||
265 | if (pat == NO_PATTERN) /* not yet mapped */ | ||
266 | { | ||
267 | pat = lcd_get_free_pat(xchar); /* try to map */ | ||
268 | if (pat == NO_PATTERN) /* failed: just use substitute */ | ||
269 | return xchar_info[xchar].hw_char; | ||
270 | else /* define pattern */ | ||
271 | lcd_define_hw_pattern(pat, xchar_to_glyph(xchar)); | ||
272 | } | ||
273 | hw_pattern[pat].count++; /* increase reference count */ | ||
274 | return pat; | ||
275 | } | ||
276 | else /* hardware char */ | ||
277 | return xchar_info[xchar].hw_char; | ||
278 | } | ||
279 | |||
280 | static void lcd_putxchar(int x, int y, int xchar) | ||
281 | { | ||
282 | int lcd_char = lcd_buffer[x][y]; | ||
283 | |||
284 | if (lcd_char < hw_pattern_count) /* old char was soft */ | ||
285 | hw_pattern[lcd_char].count--; /* decrease old reference count */ | ||
286 | |||
287 | lcd_buffer[x][y] = lcd_char = map_xchar(xchar); | ||
288 | #ifdef SIMULATOR | ||
289 | hardware_buffer_lcd[x][y] = lcd_char; | ||
290 | lcd_update(); | ||
291 | #else | ||
292 | lcd_put_hw_char(x, y, lcd_char); | ||
293 | #endif | ||
294 | } | ||
295 | |||
296 | /** user-definable pattern handling **/ | ||
297 | |||
298 | unsigned long lcd_get_locked_pattern(void) | ||
299 | { | ||
300 | int i = 0; | ||
301 | |||
302 | for (i = 0; i < VARIABLE_XCHARS; i++) | ||
303 | { | ||
304 | if (!xfont_variable_locked[i]) | ||
305 | { | ||
306 | xfont_variable_locked[i] = true; | ||
307 | return 0xe000 + i; /* hard-coded */ | ||
308 | } | ||
309 | } | ||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | void lcd_unlock_pattern(unsigned long ucs) | ||
314 | { | ||
315 | int xchar = find_xchar(ucs); | ||
316 | int index = xchar_info[xchar].glyph; | ||
317 | |||
318 | if (index & 0x8000) /* variable extended char */ | ||
319 | { | ||
320 | lcd_free_pat(xchar); | ||
321 | xfont_variable_locked[index & 0x7fff] = false; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | void lcd_define_pattern(unsigned long ucs, const char *pattern) | ||
326 | { | ||
327 | int xchar = find_xchar(ucs); | ||
328 | int index = xchar_info[xchar].glyph; | ||
329 | int pat; | ||
330 | |||
331 | if (index & 0x8000) /* variable extended char */ | ||
332 | { | ||
333 | memcpy(xfont_variable[index & 0x7fff], pattern, HW_PATTERN_SIZE); | ||
334 | pat = xchar_to_pat(xchar); | ||
335 | if (pat != NO_PATTERN) | ||
336 | lcd_define_hw_pattern(pat, pattern); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | /** output functions **/ | ||
341 | |||
342 | /* Clear the whole display */ | ||
343 | void lcd_clear_display(void) | ||
344 | { | ||
345 | int x, y; | ||
346 | int xchar = find_xchar(' '); | ||
347 | |||
348 | lcd_stop_scroll(); | ||
349 | lcd_remove_cursor(); | ||
350 | |||
351 | for (x = 0; x < LCD_WIDTH; x++) | ||
352 | for (y = 0; y < LCD_HEIGHT; y++) | ||
353 | lcd_putxchar(x, y, xchar); | ||
354 | } | ||
355 | |||
356 | /* Put an unicode character at the given position */ | ||
357 | void lcd_putc(int x, int y, unsigned long ucs) | ||
358 | { | ||
359 | if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT) | ||
360 | return; | ||
361 | |||
362 | lcd_putxchar(x, y, find_xchar(ucs)); | ||
363 | } | ||
364 | |||
365 | /* Show cursor (alternating with existing character) at the given position */ | ||
366 | void lcd_put_cursor(int x, int y, unsigned long cursor_ucs) | ||
367 | { | ||
368 | if ((unsigned)x >= LCD_WIDTH || (unsigned)y >= LCD_HEIGHT | ||
369 | || cursor.enabled) | ||
370 | return; | ||
371 | |||
372 | cursor.enabled = true; | ||
373 | cursor.visible = false; | ||
374 | cursor.hw_char = map_xchar(find_xchar(cursor_ucs)); | ||
375 | cursor.x = x; | ||
376 | cursor.y = y; | ||
377 | cursor.downcount = 0; | ||
378 | cursor.divider = 4; | ||
379 | } | ||
380 | |||
381 | /* Remove the cursor */ | ||
382 | void lcd_remove_cursor(void) | ||
383 | { | ||
384 | if (cursor.enabled) | ||
385 | { | ||
386 | if (cursor.hw_char < hw_pattern_count) /* soft char, unmap */ | ||
387 | hw_pattern[cursor.hw_char].count--; | ||
388 | |||
389 | cursor.enabled = false; | ||
390 | #ifdef SIMULATOR | ||
391 | hardware_buffer_lcd[cursor.x][cursor.y] = lcd_buffer[cursor.x][cursor.y]; | ||
392 | #else | ||
393 | lcd_put_hw_char(cursor.x, cursor.y, lcd_buffer[cursor.x][cursor.y]); | ||
394 | #endif | ||
395 | } | ||
396 | } | ||
397 | |||
398 | /* Put a string at a given position, skipping first ofs chars */ | ||
399 | static int lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) | ||
400 | { | ||
401 | unsigned short ucs; | ||
402 | const unsigned char *utf8 = str; | ||
403 | |||
404 | while (*utf8 && x < LCD_WIDTH) | ||
405 | { | ||
406 | utf8 = utf8decode(utf8, &ucs); | ||
407 | |||
408 | if (ofs > 0) | ||
409 | { | ||
410 | ofs--; | ||
411 | continue; | ||
412 | } | ||
413 | lcd_putc(x++, y, ucs); | ||
414 | } | ||
415 | return x; | ||
416 | } | ||
417 | |||
418 | /* Put a string at a given position */ | ||
419 | void lcd_putsxy(int x, int y, const unsigned char *str) | ||
420 | { | ||
421 | lcd_putsxyofs(x, y, 0, str); | ||
422 | } | ||
423 | |||
424 | /*** Line oriented text output ***/ | ||
425 | |||
426 | /* Put a string at a given char position */ | ||
427 | void lcd_puts(int x, int y, const unsigned char *str) | ||
428 | { | ||
429 | lcd_puts_offset(x, y, str, 0); | ||
430 | } | ||
431 | |||
432 | /* Put a string at a given char position, skipping first offset chars */ | ||
433 | void lcd_puts_offset(int x, int y, const unsigned char *str, int offset) | ||
434 | { | ||
435 | /* make sure scrolling is turned off on the line we are updating */ | ||
436 | scrolling_lines &= ~(1 << y); | ||
437 | |||
438 | x += xmargin; | ||
439 | y += ymargin; | ||
440 | |||
441 | x = lcd_putsxyofs(x, y, offset, str); | ||
442 | while (x < LCD_WIDTH) | ||
443 | lcd_putc(x++, y, ' '); | ||
444 | } | ||
445 | |||
446 | /** scrolling **/ | ||
447 | |||
448 | void lcd_stop_scroll(void) | ||
449 | { | ||
450 | scrolling_lines=0; | ||
451 | } | ||
452 | |||
453 | void lcd_scroll_speed(int speed) | ||
454 | { | ||
455 | scroll_ticks = scroll_tick_table[speed]; | ||
456 | } | ||
457 | |||
458 | void lcd_scroll_delay(int ms) | ||
459 | { | ||
460 | scroll_delay = ms / (HZ / 10); | ||
461 | } | ||
462 | |||
463 | void lcd_bidir_scroll(int percent) | ||
464 | { | ||
465 | bidir_limit = percent; | ||
466 | } | ||
467 | |||
468 | void lcd_jump_scroll(int mode) /* 0=off, 1=once, ..., JUMP_SCROLL_ALWAYS */ | ||
469 | { | ||
470 | jump_scroll = mode; | ||
471 | } | ||
472 | |||
473 | void lcd_jump_scroll_delay(int ms) | ||
474 | { | ||
475 | jump_scroll_delay = ms / (HZ / 10); | ||
476 | } | ||
477 | |||
478 | void lcd_puts_scroll(int x, int y, const unsigned char *string) | ||
479 | { | ||
480 | lcd_puts_scroll_offset(x, y, string, 0); | ||
481 | } | ||
482 | |||
483 | void lcd_puts_scroll_offset(int x, int y, const unsigned char *string, | ||
484 | int offset) | ||
485 | { | ||
486 | struct scrollinfo* s; | ||
487 | int len; | ||
488 | |||
489 | s = &scroll[y]; | ||
490 | |||
491 | s->start_tick = current_tick + scroll_delay; | ||
492 | |||
493 | lcd_puts_offset(x, y, string, offset); | ||
494 | len = utf8length(string); | ||
495 | |||
496 | if (LCD_WIDTH - xmargin < len) | ||
497 | { | ||
498 | /* prepare scroll line */ | ||
499 | char *end; | ||
500 | |||
501 | memset(s->line, 0, sizeof s->line); | ||
502 | strcpy(s->line, string); | ||
503 | |||
504 | /* get width */ | ||
505 | s->len = utf8length(s->line); | ||
506 | |||
507 | /* scroll bidirectional or forward only depending on the string width */ | ||
508 | if (bidir_limit) | ||
509 | { | ||
510 | s->bidir = s->len < (LCD_WIDTH - xmargin) * (100 + bidir_limit) / 100; | ||
511 | } | ||
512 | else | ||
513 | s->bidir = false; | ||
514 | |||
515 | if (!s->bidir) /* add spaces if scrolling in the round */ | ||
516 | { | ||
517 | strcat(s->line, " "); | ||
518 | /* get new width incl. spaces */ | ||
519 | s->len += SCROLL_SPACING; | ||
520 | } | ||
521 | |||
522 | end = strchr(s->line, '\0'); | ||
523 | strncpy(end, string, LCD_WIDTH); | ||
524 | |||
525 | s->offset = offset; | ||
526 | s->startx = xmargin + x; | ||
527 | s->backward = false; | ||
528 | scrolling_lines |= (1<<y); | ||
529 | } | ||
530 | else | ||
531 | /* force a bit switch-off since it doesn't scroll */ | ||
532 | scrolling_lines &= ~(1<<y); | ||
533 | } | ||
534 | |||
535 | static void scroll_thread(void) | ||
536 | { | ||
537 | struct scrollinfo* s; | ||
538 | int index; | ||
539 | int xpos, ypos; | ||
540 | |||
541 | /* initialize scroll struct array */ | ||
542 | scrolling_lines = 0; | ||
543 | |||
544 | while (1) | ||
545 | { | ||
546 | for (index = 0; index < SCROLLABLE_LINES; index++) | ||
547 | { | ||
548 | /* really scroll? */ | ||
549 | if (!(scrolling_lines&(1<<index))) | ||
550 | continue; | ||
551 | |||
552 | s = &scroll[index]; | ||
553 | |||
554 | /* check pause */ | ||
555 | if (TIME_BEFORE(current_tick, s->start_tick)) | ||
556 | continue; | ||
557 | |||
558 | if (s->backward) | ||
559 | s->offset--; | ||
560 | else | ||
561 | s->offset++; | ||
562 | |||
563 | xpos = s->startx; | ||
564 | ypos = ymargin + index; | ||
565 | |||
566 | if (s->bidir) /* scroll bidirectional */ | ||
567 | { | ||
568 | if (s->offset <= 0) | ||
569 | { | ||
570 | /* at beginning of line */ | ||
571 | s->offset = 0; | ||
572 | s->backward = false; | ||
573 | s->start_tick = current_tick + scroll_delay * 2; | ||
574 | } | ||
575 | if (s->offset >= s->len - (LCD_WIDTH - xpos)) | ||
576 | { | ||
577 | /* at end of line */ | ||
578 | s->offset = s->len - (LCD_WIDTH - xpos); | ||
579 | s->backward = true; | ||
580 | s->start_tick = current_tick + scroll_delay * 2; | ||
581 | } | ||
582 | } | ||
583 | else /* scroll forward the whole time */ | ||
584 | { | ||
585 | if (s->offset >= s->len) | ||
586 | s->offset -= s->len; | ||
587 | } | ||
588 | lcd_putsxyofs(xpos, ypos, s->offset, s->line); | ||
589 | } | ||
590 | if (cursor.enabled) | ||
591 | { | ||
592 | if (--cursor.downcount < 0) | ||
593 | { | ||
594 | int lcd_char; | ||
595 | |||
596 | cursor.downcount = cursor.divider; | ||
597 | cursor.visible = !cursor.visible; | ||
598 | lcd_char = cursor.visible ? cursor.hw_char | ||
599 | : lcd_buffer[cursor.x][cursor.y]; | ||
600 | #ifdef SIMULATOR | ||
601 | hardware_buffer_lcd[cursor.x][cursor.y] = lcd_char; | ||
602 | #else | ||
603 | lcd_put_hw_char(cursor.x, cursor.y, lcd_char); | ||
604 | #endif | ||
605 | } | ||
606 | } | ||
607 | #ifdef SIMULATOR | ||
608 | lcd_update(); | ||
609 | #endif | ||
610 | sleep(scroll_ticks); | ||
611 | } | ||
612 | } | ||